Seats, part II

- Any living entity can now use seats
- Fix client sync issues with seats
- Fixed contraptions double-reversing roll and pitch values when communicating to the collision engine
- Seats now transfer their passengers to a contraption when moved and back when disassembled
- Attempted further refinements to the collision response of horizontally rotated contraptions
- Set up a hook to inject custom interaction between players and contraption mounted blocks on right-click
- Seats can now by mounted by players while assembled to a contraption
- Minor refactors to the contraption class
This commit is contained in:
simibubi 2020-07-22 01:18:09 +02:00
parent d3e7b23d6e
commit 7994835cb0
35 changed files with 1231 additions and 705 deletions

View file

@ -0,0 +1,89 @@
import org.apache.commons.lang3.mutable.MutableObject;
import com.simibubi.create.content.contraptions.components.structureMovement.Contraption;
import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionEntity;
import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionInteractionPacket;
import com.simibubi.create.foundation.networking.AllPackets;
import com.simibubi.create.foundation.utility.RaycastHelper;
import com.simibubi.create.foundation.utility.RaycastHelper.PredicateTraceResult;
import net.minecraft.block.BlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.client.entity.player.ClientPlayerEntity;
import net.minecraft.util.Direction;
import net.minecraft.util.Hand;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.world.gen.feature.template.Template.BlockInfo;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.client.event.InputEvent.ClickInputEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
@EventBusSubscriber
public class ContraptionInteractionHandler {
@SubscribeEvent
@OnlyIn(Dist.CLIENT)
public static void rightClickingOnContraptionsGetsHandledLocally(ClickInputEvent event) {
Minecraft mc = Minecraft.getInstance();
ClientPlayerEntity player = mc.player;
if (player == null)
return;
if (mc.world == null)
return;
if (!event.isUseItem())
return;
Vec3d origin = RaycastHelper.getTraceOrigin(player);
double reach = mc.playerController.getBlockReachDistance();
if (mc.objectMouseOver != null && mc.objectMouseOver.getHitVec() != null)
reach = Math.min(mc.objectMouseOver.getHitVec().distanceTo(origin), reach);
Vec3d target = RaycastHelper.getTraceTarget(player, reach, origin);
for (ContraptionEntity contraptionEntity : mc.world.getEntitiesWithinAABB(ContraptionEntity.class,
new AxisAlignedBB(origin, target))) {
Vec3d localOrigin = contraptionEntity.toLocalVector(origin);
Vec3d localTarget = contraptionEntity.toLocalVector(target);
Contraption contraption = contraptionEntity.getContraption();
MutableObject<BlockRayTraceResult> mutableResult = new MutableObject<>();
PredicateTraceResult predicateResult = RaycastHelper.rayTraceUntil(localOrigin, localTarget, p -> {
BlockInfo blockInfo = contraption.blocks.get(p);
if (blockInfo == null)
return false;
BlockState state = blockInfo.state;
VoxelShape raytraceShape = state.getShape(Minecraft.getInstance().world, BlockPos.ZERO.down());
if (raytraceShape.isEmpty())
return false;
BlockRayTraceResult rayTrace = raytraceShape.rayTrace(localOrigin, localTarget, p);
if (rayTrace != null) {
mutableResult.setValue(rayTrace);
return true;
}
return false;
});
if (predicateResult == null || predicateResult.missed())
return;
BlockRayTraceResult rayTraceResult = mutableResult.getValue();
Hand hand = event.getHand();
Direction face = rayTraceResult.getFace();
BlockPos pos = rayTraceResult.getPos();
if (!contraptionEntity.handlePlayerInteraction(player, pos, face, hand))
return;
AllPackets.channel.sendToServer(new ContraptionInteractionPacket(contraptionEntity, hand,
pos, face));
event.setCanceled(true);
event.setSwingHand(false);
}
}
}

View file

@ -26,7 +26,7 @@ public class AllEntityTypes {
public static final RegistryEntry<EntityType<SuperGlueEntity>> SUPER_GLUE =
register("super_glue", SuperGlueEntity::new, EntityClassification.MISC, 10, Integer.MAX_VALUE, false, SuperGlueEntity::build);
public static final RegistryEntry<EntityType<SeatEntity>> SEAT =
register("seat", SeatEntity::new, EntityClassification.MISC, 0, Integer.MAX_VALUE, false, SeatEntity::build);
register("seat", SeatEntity::new, EntityClassification.MISC, 5, Integer.MAX_VALUE, false, SeatEntity::build);
private static <T extends Entity> RegistryEntry<EntityType<T>> register(String name, IFactory<T> factory,
EntityClassification group, int range, int updateFrequency, boolean sendVelocity,

View file

@ -1,17 +1,25 @@
package com.simibubi.create.content.contraptions.components.actors;
import java.util.List;
import com.simibubi.create.AllShapes;
import com.simibubi.create.content.contraptions.components.structureMovement.IPortableBlock;
import com.simibubi.create.content.contraptions.components.structureMovement.MovementBehaviour;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.MobEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemGroup;
import net.minecraft.item.ItemStack;
import net.minecraft.pathfinding.PathNodeType;
import net.minecraft.util.ActionResultType;
import net.minecraft.util.Hand;
import net.minecraft.util.NonNullList;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.BlockRayTraceResult;
import net.minecraft.util.math.shapes.ISelectionContext;
@ -19,15 +27,16 @@ import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.World;
public class SeatBlock extends Block {
public class SeatBlock extends Block implements IPortableBlock {
public static MovementBehaviour MOVEMENT = new SeatMovementBehaviour();
private boolean inCreativeTab;
public SeatBlock(Properties p_i48440_1_, boolean inCreativeTab) {
super(p_i48440_1_);
this.inCreativeTab = inCreativeTab;
}
@Override
public void fillItemGroup(ItemGroup group, NonNullList<ItemStack> p_149666_2_) {
if (group != ItemGroup.SEARCH && !inCreativeTab)
@ -41,8 +50,21 @@ public class SeatBlock extends Block {
}
@Override
public void onLanded(IBlockReader p_176216_1_, Entity p_176216_2_) {
Blocks.PINK_BED.onLanded(p_176216_1_, p_176216_2_);
public void onLanded(IBlockReader reader, Entity entity) {
BlockPos pos = entity.getPosition();
if (entity instanceof PlayerEntity || !(entity instanceof LivingEntity) || isSeatOccupied(entity.world, pos)) {
Blocks.PINK_BED.onLanded(reader, entity);
return;
}
if (reader.getBlockState(pos)
.getBlock() != this)
return;
sitDown(entity.world, pos, entity);
}
@Override
public PathNodeType getAiPathNodeType(BlockState state, IBlockReader world, BlockPos pos, MobEntity entity) {
return PathNodeType.RAIL;
}
@Override
@ -52,17 +74,46 @@ public class SeatBlock extends Block {
}
@Override
public ActionResultType onUse(BlockState p_225533_1_, World world, BlockPos pos, PlayerEntity player, Hand p_225533_5_, BlockRayTraceResult p_225533_6_) {
if (SeatEntity.TAKEN.containsKey(pos))
return ActionResultType.FAIL;
public ActionResultType onUse(BlockState p_225533_1_, World world, BlockPos pos, PlayerEntity player,
Hand p_225533_5_, BlockRayTraceResult p_225533_6_) {
if (player.isSneaking())
return ActionResultType.PASS;
List<SeatEntity> seats = world.getEntitiesWithinAABB(SeatEntity.class, new AxisAlignedBB(pos));
if (!seats.isEmpty()) {
SeatEntity seatEntity = seats.get(0);
List<Entity> passengers = seatEntity.getPassengers();
if (!passengers.isEmpty() && passengers.get(0) instanceof PlayerEntity)
return ActionResultType.PASS;
if (!world.isRemote) {
seatEntity.removePassengers();
player.startRiding(seatEntity);
}
return ActionResultType.SUCCESS;
}
if (world.isRemote)
return ActionResultType.SUCCESS;
SeatEntity seat = new SeatEntity(world, pos);
world.addEntity(seat);
player.startRiding(seat);
sitDown(world, pos, player);
return ActionResultType.SUCCESS;
}
public static boolean isSeatOccupied(World world, BlockPos pos) {
return !world.getEntitiesWithinAABB(SeatEntity.class, new AxisAlignedBB(pos))
.isEmpty();
}
public static void sitDown(World world, BlockPos pos, Entity entity) {
if (world.isRemote)
return;
SeatEntity seat = new SeatEntity(world, pos);
seat.setPos(pos.getX() + .5f, pos.getY(), pos.getZ() + .5f);
world.addEntity(seat);
entity.startRiding(seat, true);
}
@Override
public MovementBehaviour getMovementBehaviour() {
return MOVEMENT;
}
}

View file

@ -1,6 +1,7 @@
package com.simibubi.create.content.contraptions.components.actors;
import com.simibubi.create.AllEntityTypes;
import net.minecraft.client.renderer.culling.ClippingHelperImpl;
import net.minecraft.client.renderer.entity.EntityRenderer;
import net.minecraft.client.renderer.entity.EntityRendererManager;
@ -8,18 +9,16 @@ import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.network.IPacket;
import net.minecraft.network.PacketBuffer;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData;
import net.minecraftforge.fml.network.NetworkHooks;
import java.util.HashMap;
import java.util.Map;
public class SeatEntity extends Entity {
public static final Map<BlockPos, SeatEntity> TAKEN = new HashMap<>();
public class SeatEntity extends Entity implements IEntityAdditionalSpawnData {
public SeatEntity(EntityType<?> p_i48580_1_, World p_i48580_2_) {
super(p_i48580_1_, p_i48580_2_);
@ -27,28 +26,39 @@ public class SeatEntity extends Entity {
public SeatEntity(World world, BlockPos pos) {
this(AllEntityTypes.SEAT.get(), world);
this.setPos(pos.getX() + 0.5, pos.getY() + 0.30, pos.getZ() + 0.5);
noClip = true;
TAKEN.put(pos, this);
}
public static EntityType.Builder<?> build(EntityType.Builder<?> builder) {
@SuppressWarnings("unchecked")
EntityType.Builder<SeatEntity> entityBuilder = (EntityType.Builder<SeatEntity>) builder;
return entityBuilder.size(0, 0);
return entityBuilder.size(0.25f, 0.35f);
}
@Override
public AxisAlignedBB getBoundingBox() {
return super.getBoundingBox();
}
@Override
public void setPos(double x, double y, double z) {
super.setPos(x, y, z);
AxisAlignedBB bb = getBoundingBox();
Vec3d diff = new Vec3d(x, y, z).subtract(bb.getCenter());
setBoundingBox(bb.offset(diff));
}
@Override
public void setMotion(Vec3d p_213317_1_) {}
@Override
public void tick() {
if (world.isRemote)
if (world.isRemote)
return;
BlockPos blockPos = new BlockPos(getX(), getY(), getZ());
if (isBeingRidden() && world.getBlockState(blockPos).getBlock() instanceof SeatBlock)
boolean blockPresent = world.getBlockState(getPosition())
.getBlock() instanceof SeatBlock;
if (isBeingRidden() && blockPresent)
return;
TAKEN.remove(blockPos);
this.remove();
}
@ -61,7 +71,7 @@ public class SeatEntity extends Entity {
protected void removePassenger(Entity entity) {
super.removePassenger(entity);
Vec3d pos = entity.getPositionVec();
entity.setPosition(pos.x, pos.y + 0.7, pos.z);
entity.setPosition(pos.x, pos.y + 0.85f, pos.z);
}
@Override
@ -85,7 +95,8 @@ public class SeatEntity extends Entity {
}
@Override
public boolean shouldRender(SeatEntity p_225626_1_, ClippingHelperImpl p_225626_2_, double p_225626_3_, double p_225626_5_, double p_225626_7_) {
public boolean shouldRender(SeatEntity p_225626_1_, ClippingHelperImpl p_225626_2_, double p_225626_3_,
double p_225626_5_, double p_225626_7_) {
return false;
}
@ -94,4 +105,10 @@ public class SeatEntity extends Entity {
return null;
}
}
@Override
public void writeSpawnData(PacketBuffer buffer) {}
@Override
public void readSpawnData(PacketBuffer additionalData) {}
}

View file

@ -0,0 +1,9 @@
package com.simibubi.create.content.contraptions.components.actors;
import com.simibubi.create.content.contraptions.components.structureMovement.MovementBehaviour;
public class SeatMovementBehaviour extends MovementBehaviour {
}

View file

@ -24,6 +24,7 @@ import com.simibubi.create.foundation.collision.ContinuousOBBCollider.Continuous
import com.simibubi.create.foundation.collision.Matrix3d;
import com.simibubi.create.foundation.collision.OrientedBB;
import com.simibubi.create.foundation.utility.AngleHelper;
import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.block.BlockState;
@ -32,9 +33,11 @@ import net.minecraft.client.Minecraft;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.MoverType;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.util.DamageSource;
import net.minecraft.util.Direction;
import net.minecraft.util.Direction.Axis;
@ -51,10 +54,12 @@ import net.minecraft.world.World;
import net.minecraft.world.gen.feature.template.Template.BlockInfo;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.common.util.Constants.NBT;
import net.minecraftforge.event.TickEvent.ClientTickEvent;
import net.minecraftforge.event.TickEvent.Phase;
import net.minecraftforge.event.TickEvent.WorldTickEvent;
import net.minecraftforge.event.entity.EntityJoinWorldEvent;
import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.DistExecutor;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
@ -66,7 +71,7 @@ public class ContraptionCollider {
new DamageSource("create.contraption_suffocate").setDamageBypassesArmor();
public static boolean wasClientPlayerGrounded;
public static Cache<World, List<WeakReference<ContraptionEntity>>> activeContraptions = CacheBuilder.newBuilder()
.expireAfterAccess(40, SECONDS)
.expireAfterAccess(400, SECONDS)
.build();
@SubscribeEvent
@ -102,6 +107,22 @@ public class ContraptionCollider {
runCollisions(world);
}
@SubscribeEvent
public static void entitiesWhoJustDismountedGetSentToTheRightLocation(LivingUpdateEvent event) {
LivingEntity entityLiving = event.getEntityLiving();
if (entityLiving == null)
return;
if (entityLiving.world.isRemote)
return;
CompoundNBT data = entityLiving.getPersistentData();
if (!data.contains("ContraptionDismountLocation"))
return;
Vec3d position = VecHelper.readNBT(data.getList("ContraptionDismountLocation", NBT.TAG_DOUBLE));
if (entityLiving.getRidingEntity() == null)
entityLiving.setPositionAndUpdate(position.x, position.y, position.z);
data.remove("ContraptionDismountLocation");
}
private static void runCollisions(World world) {
List<WeakReference<ContraptionEntity>> list = activeContraptions.getIfPresent(world);
if (list == null)
@ -131,9 +152,9 @@ public class ContraptionCollider {
return;
Vec3d centerOfBlock = VecHelper.getCenterOf(BlockPos.ZERO);
double conRotX = contraptionRotation.z;
double conRotX = contraptionRotation.x;
double conRotY = contraptionRotation.y;
double conRotZ = contraptionRotation.x;
double conRotZ = contraptionRotation.z;
Vec3d conMotion = contraptionPosition.subtract(contraptionEntity.getPrevPositionVec());
Vec3d conAngularMotion = contraptionRotation.subtract(contraptionEntity.getPrevRotationVec());
Vec3d contraptionCentreOffset = contraptionEntity.stationary ? centerOfBlock : Vec3d.ZERO.add(0, 0.5, 0);
@ -180,6 +201,7 @@ public class ContraptionCollider {
// Prepare entity bounds
OrientedBB obb = new OrientedBB(localBB);
obb.setRotation(rotation);
motion = motion.subtract(conMotion);
motion = rotation.transform(motion);
// Vec3d visualizerOrigin = new Vec3d(10, 64, 0);
@ -198,41 +220,48 @@ public class ContraptionCollider {
.forEach(shape -> shape.toBoundingBoxList()
.forEach(bbs::add));
for (AxisAlignedBB bb : bbs) {
Vec3d currentResponse = collisionResponse.getValue();
obb.setCenter(obbCenter.add(currentResponse));
ContinuousSeparationManifold intersect = obb.intersect(bb, allowedMotion.getValue());
// OutlineParams params = CreateClient.outliner.showAABB(bb, bb.offset(visualizerOrigin))
// .withFaceTexture(AllSpecialTextures.HIGHLIGHT_CHECKERED);
// params.colored(0xffffff);
boolean doHorizontalPass = conRotX == 0 && conRotZ == 0;
for (boolean horizontalPass : Iterate.trueAndFalse) {
if (intersect == null)
continue;
if (surfaceCollision.isFalse())
surfaceCollision.setValue(intersect.isSurfaceCollision());
for (AxisAlignedBB bb : bbs) {
Vec3d currentResponse = collisionResponse.getValue();
obb.setCenter(obbCenter.add(currentResponse));
ContinuousSeparationManifold intersect = obb.intersect(bb, allowedMotion.getValue());
double timeOfImpact = intersect.getTimeOfImpact();
if (timeOfImpact > 0 && timeOfImpact < 1) {
futureCollision.setTrue();
// Vec3d prev = allowedMotion.getValue();
allowedMotion.setValue(intersect.getAllowedMotion(allowedMotion.getValue()));
// Debug.debugChat("Allowed Motion FROM " + prev.toString());
// Debug.debugChat("Allowed Motion TO " + allowedMotion.getValue()
// .toString());
// params.colored(0x4499ff);
continue;
}
Vec3d separation = intersect.asSeparationVec(entity.stepHeight);
if (separation != null && !separation.equals(Vec3d.ZERO)) {
collisionResponse.setValue(currentResponse.add(separation));
// Debug.debugChat("Collision " + currentResponse.add(separation)
// .toString());
// params.colored(0xff9944);
if (intersect == null)
continue;
if ((!horizontalPass || !doHorizontalPass) && surfaceCollision.isFalse())
surfaceCollision.setValue(intersect.isSurfaceCollision());
double timeOfImpact = intersect.getTimeOfImpact();
if (timeOfImpact > 0 && timeOfImpact < 1) {
futureCollision.setTrue();
allowedMotion.setValue(intersect.getAllowedMotion(allowedMotion.getValue()));
continue;
}
Vec3d separation = intersect.asSeparationVec(entity.stepHeight);
if (separation != null && !separation.equals(Vec3d.ZERO))
collisionResponse.setValue(currentResponse.add(separation));
}
if (!horizontalPass || !doHorizontalPass)
break;
boolean noVerticalMotionResponse = allowedMotion.getValue().y == motion.y;
boolean noVerticalCollision = collisionResponse.getValue().y == 0;
if (noVerticalCollision && noVerticalMotionResponse)
break;
// Re-run collisions with horizontal offset
collisionResponse.setValue(collisionResponse.getValue()
.mul(1, 0, 1));
allowedMotion.setValue(allowedMotion.getValue()
.mul(1, 0, 1)
.add(0, motion.y, 0));
continue;
}
// Debug.debugChat("----");
// Resolve collision
Vec3d entityMotion = entity.getMotion();
Vec3d totalResponse = collisionResponse.getValue();
@ -240,7 +269,8 @@ public class ContraptionCollider {
boolean hardCollision = !totalResponse.equals(Vec3d.ZERO);
rotation.transpose();
motionResponse = rotation.transform(motionResponse);
motionResponse = rotation.transform(motionResponse)
.add(conMotion);
totalResponse = rotation.transform(totalResponse);
rotation.transpose();
@ -262,7 +292,7 @@ public class ContraptionCollider {
Vec3d contactPoint = entityPosition.subtract(contraptionCentreOffset)
.subtract(contraptionPosition);
contactPoint =
VecHelper.rotate(contactPoint, conAngularMotion.z, conAngularMotion.y, conAngularMotion.x);
VecHelper.rotate(contactPoint, conAngularMotion.x, conAngularMotion.y, conAngularMotion.z);
contactPoint = contactPoint.add(contraptionPosition)
.add(contraptionCentreOffset)
.add(conMotion);

View file

@ -5,12 +5,15 @@ import static com.simibubi.create.foundation.utility.AngleHelper.getShortestAngl
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import java.util.UUID;
import java.util.stream.Stream;
import org.apache.commons.lang3.tuple.MutablePair;
import com.google.common.collect.ImmutableSet;
import com.simibubi.create.AllEntityTypes;
import com.simibubi.create.content.contraptions.components.actors.SeatEntity;
import com.simibubi.create.content.contraptions.components.structureMovement.bearing.BearingContraption;
import com.simibubi.create.content.contraptions.components.structureMovement.glue.SuperGlueEntity;
import com.simibubi.create.content.contraptions.components.structureMovement.mounted.CartAssemblerTileEntity.CartMovementMode;
@ -45,6 +48,7 @@ import net.minecraft.tags.BlockTags;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.DamageSource;
import net.minecraft.util.Direction;
import net.minecraft.util.Hand;
import net.minecraft.util.ReuseableStream;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
@ -70,7 +74,7 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
protected BlockPos controllerPos;
protected Vec3d motionBeforeStall;
protected boolean stationary;
protected boolean initialized;
final List<Entity> collidingEntities = new ArrayList<>();
private static final Ingredient FUEL_ITEMS = Ingredient.fromItems(Items.COAL, Items.CHARCOAL);
@ -98,11 +102,9 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
public static ContraptionEntity createMounted(World world, Contraption contraption, float initialAngle) {
ContraptionEntity entity = new ContraptionEntity(AllEntityTypes.CONTRAPTION.get(), world);
entity.contraption = contraption;
entity.contraptionCreated(contraption);
entity.initialAngle = initialAngle;
entity.forceYaw(initialAngle);
if (contraption != null)
contraption.gatherStoredItems();
return entity;
}
@ -116,12 +118,25 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
public static ContraptionEntity createStationary(World world, Contraption contraption) {
ContraptionEntity entity = new ContraptionEntity(AllEntityTypes.STATIONARY_CONTRAPTION.get(), world);
entity.contraption = contraption;
if (contraption != null)
contraption.gatherStoredItems();
entity.contraptionCreated(contraption);
return entity;
}
protected void contraptionCreated(Contraption contraption) {
this.contraption = contraption;
if (contraption == null)
return;
if (world.isRemote)
return;
contraption.gatherStoredItems();
}
protected void contraptionInitialize() {
if (!world.isRemote)
contraption.mountPassengers(this);
initialized = true;
}
public <T extends TileEntity & IControlContraption> ContraptionEntity controlledBy(T controller) {
this.controllerPos = controller.getPos();
return this;
@ -142,6 +157,110 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
return true;
}
@Override
protected void addPassenger(Entity passenger) {
super.addPassenger(passenger);
}
public void addSittingPassenger(Entity passenger, int seatIndex) {
passenger.startRiding(this, true);
if (world.isRemote)
return;
contraption.seatMapping.put(passenger.getUniqueID(), seatIndex);
AllPackets.channel.send(PacketDistributor.TRACKING_ENTITY.with(() -> this),
new ContraptionSeatMappingPacket(getEntityId(), contraption.seatMapping));
}
@Override
protected void removePassenger(Entity passenger) {
Vec3d transformedVector = getPassengerPosition(passenger);
super.removePassenger(passenger);
if (world.isRemote)
return;
if (transformedVector != null)
passenger.getPersistentData()
.put("ContraptionDismountLocation", VecHelper.writeNBT(transformedVector));
contraption.seatMapping.remove(passenger.getUniqueID());
AllPackets.channel.send(PacketDistributor.TRACKING_ENTITY.with(() -> this),
new ContraptionSeatMappingPacket(getEntityId(), contraption.seatMapping));
}
@Override
public void updatePassengerPosition(Entity passenger, IMoveCallback callback) {
if (!isPassenger(passenger))
return;
Vec3d transformedVector = getPassengerPosition(passenger);
if (transformedVector == null)
return;
callback.accept(passenger, transformedVector.x, transformedVector.y, transformedVector.z);
}
protected Vec3d getPassengerPosition(Entity passenger) {
AxisAlignedBB bb = passenger.getBoundingBox();
double ySize = bb.getYSize();
BlockPos seat = contraption.getSeat(passenger.getUniqueID());
if (seat == null)
return null;
Vec3d transformedVector = toGlobalVector(new Vec3d(seat).add(.5, passenger.getYOffset() + ySize - .15f, .5))
.add(VecHelper.getCenterOf(BlockPos.ZERO))
.subtract(0.5, ySize, 0.5);
return transformedVector;
}
@Override
protected boolean canFitPassenger(Entity p_184219_1_) {
return getPassengers().size() < contraption.seats.size();
}
public boolean handlePlayerInteraction(PlayerEntity player, BlockPos localPos, Direction side,
Hand interactionHand) {
int indexOfSeat = contraption.seats.indexOf(localPos);
if (indexOfSeat == -1)
return false;
// Eject potential existing passenger
for (Entry<UUID, Integer> entry : contraption.seatMapping.entrySet()) {
if (entry.getValue() != indexOfSeat)
continue;
for (Entity entity : getPassengers()) {
if (!entry.getKey().equals(entity.getUniqueID()))
continue;
if (entity instanceof PlayerEntity)
return false;
if (!world.isRemote) {
Vec3d transformedVector = getPassengerPosition(entity);
entity.stopRiding();
if (transformedVector != null)
entity.setPositionAndUpdate(transformedVector.x, transformedVector.y, transformedVector.z);
}
}
}
if (world.isRemote)
return true;
addSittingPassenger(player, indexOfSeat);
return true;
}
public Vec3d toGlobalVector(Vec3d localVec) {
Vec3d rotationOffset = VecHelper.getCenterOf(BlockPos.ZERO);
localVec = localVec.subtract(rotationOffset);
localVec = VecHelper.rotate(localVec, getRotationVec());
localVec = localVec.add(rotationOffset)
.add(getAnchorVec());
return localVec;
}
public Vec3d toLocalVector(Vec3d globalVec) {
Vec3d rotationOffset = VecHelper.getCenterOf(BlockPos.ZERO);
globalVec = globalVec.subtract(getAnchorVec())
.subtract(rotationOffset);
globalVec = VecHelper.rotate(globalVec, getRotationVec().scale(-1));
globalVec = globalVec.add(rotationOffset);
return globalVec;
}
@Override
public void tick() {
if (contraption == null) {
@ -149,6 +268,9 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
return;
}
if (!initialized)
contraptionInitialize();
checkController();
Entity mountedEntity = getRidingEntity();
@ -173,10 +295,6 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
super.tick();
}
public void collisionTick() {
// ContraptionCollider.collideEntities(this);
}
public void tickAsPassenger(Entity e) {
boolean rotationLock = false;
boolean pauseWhileRotating = false;
@ -266,10 +384,8 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
}
public void tickActors(Vec3d movementVector) {
float anglePitch = getPitch(1);
float angleYaw = getYaw(1);
float angleRoll = getRoll(1);
Vec3d rotationVec = new Vec3d(angleRoll, angleYaw, anglePitch);
Vec3d rotationVec = getRotationVec();
Vec3d reversedRotationVec = rotationVec.scale(-1);
Vec3d rotationOffset = VecHelper.getCenterOf(BlockPos.ZERO);
boolean stalledPreviously = contraption.stalled;
@ -283,7 +399,7 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
Vec3d actorPosition = new Vec3d(blockInfo.pos);
actorPosition = actorPosition.add(actor.getActiveAreaOffset(context));
actorPosition = VecHelper.rotate(actorPosition, angleRoll, angleYaw, anglePitch);
actorPosition = VecHelper.rotate(actorPosition, rotationVec);
actorPosition = actorPosition.add(rotationOffset)
.add(getAnchorVec());
@ -295,7 +411,7 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
if (previousPosition != null) {
context.motion = actorPosition.subtract(previousPosition);
Vec3d relativeMotion = context.motion;
relativeMotion = VecHelper.rotate(relativeMotion, -angleRoll, -angleYaw, -anglePitch);
relativeMotion = VecHelper.rotate(relativeMotion, reversedRotationVec);
context.relativeMotion = relativeMotion;
newPosVisited = !new BlockPos(previousPosition).equals(gridPosition)
|| context.relativeMotion.length() > 0 && context.firstMovement;
@ -429,6 +545,7 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
@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);
@ -479,6 +596,7 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
compound.putFloat("InitialAngle", initialAngle);
compound.putBoolean("Stalled", isStalled());
compound.putBoolean("Initialized", initialized);
}
@Override
@ -499,15 +617,15 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
}
public void disassemble() {
if (!isAlive()) {
if (!isAlive())
return;
}
if (getContraption() != null) {
remove();
BlockPos offset = new BlockPos(getAnchorVec().add(.5, .5, .5));
Vec3d rotation = new Vec3d(getRoll(1), getYaw(1), getPitch(1));
getContraption().addBlocksToWorld(world, offset, rotation);
preventMovedEntitiesFromGettingStuck();
Vec3d rotation = getRotationVec();
setBoundingBox(new AxisAlignedBB(0, 300, 0, 0, 300, 0));
contraption.addBlocksToWorld(world, offset, rotation, getPassengers());
// preventMovedEntitiesFromGettingStuck();
}
}
@ -633,11 +751,11 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
}
public Vec3d getRotationVec() {
return new Vec3d(getPitch(1), getYaw(1), getRoll(1));
return new Vec3d(getRoll(1), getYaw(1), getPitch(1));
}
public Vec3d getPrevRotationVec() {
return new Vec3d(getPitch(0), getYaw(0), getRoll(0));
return new Vec3d(getRoll(0), getYaw(0), getPitch(0));
}
public Vec3d getPrevPositionVec() {
@ -653,8 +771,12 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD
return false;
if (e instanceof SuperGlueEntity)
return false;
if (e instanceof SeatEntity)
return false;
if (e instanceof IProjectile)
return false;
if (e.getRidingEntity() != null)
return false;
Entity riding = this.getRidingEntity();
while (riding != null) {

View file

@ -0,0 +1,65 @@
package com.simibubi.create.content.contraptions.components.structureMovement;
import java.util.function.Supplier;
import com.simibubi.create.foundation.networking.SimplePacketBase;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.ServerPlayerEntity;
import net.minecraft.network.PacketBuffer;
import net.minecraft.util.Direction;
import net.minecraft.util.Hand;
import net.minecraft.util.math.BlockPos;
import net.minecraftforge.fml.network.NetworkEvent.Context;
public class ContraptionInteractionPacket extends SimplePacketBase {
private Hand interactionHand;
private int target;
private BlockPos localPos;
private Direction face;
public ContraptionInteractionPacket(ContraptionEntity target, Hand hand, BlockPos localPos, Direction side) {
this.interactionHand = hand;
this.localPos = localPos;
this.target = target.getEntityId();
this.face = side;
}
public ContraptionInteractionPacket(PacketBuffer buffer) {
target = buffer.readInt();
int handId = buffer.readInt();
interactionHand = handId == -1 ? null : Hand.values()[handId];
localPos = buffer.readBlockPos();
face = Direction.byIndex(buffer.readShort());
}
@Override
public void write(PacketBuffer buffer) {
buffer.writeInt(target);
buffer.writeInt(interactionHand == null ? -1 : interactionHand.ordinal());
buffer.writeBlockPos(localPos);
buffer.writeShort(face.getIndex());
}
@Override
public void handle(Supplier<Context> context) {
context.get()
.enqueueWork(() -> {
ServerPlayerEntity sender = context.get()
.getSender();
if (sender == null)
return;
Entity entityByID = sender.getServerWorld()
.getEntityByID(target);
if (!(entityByID instanceof ContraptionEntity))
return;
ContraptionEntity contraptionEntity = (ContraptionEntity) entityByID;
if (contraptionEntity.handlePlayerInteraction(sender, localPos, face, interactionHand))
sender.swingHand(interactionHand, true);
});
context.get()
.setPacketHandled(true);
}
}

View file

@ -0,0 +1,58 @@
package com.simibubi.create.content.contraptions.components.structureMovement;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.function.Supplier;
import com.simibubi.create.foundation.networking.SimplePacketBase;
import net.minecraft.client.Minecraft;
import net.minecraft.entity.Entity;
import net.minecraft.network.PacketBuffer;
import net.minecraftforge.fml.network.NetworkEvent.Context;
public class ContraptionSeatMappingPacket extends SimplePacketBase {
private Map<UUID, Integer> mapping;
private int entityID;
public ContraptionSeatMappingPacket(int entityID, Map<UUID, Integer> mapping) {
this.entityID = entityID;
this.mapping = mapping;
}
public ContraptionSeatMappingPacket(PacketBuffer buffer) {
entityID = buffer.readInt();
mapping = new HashMap<>();
short size = buffer.readShort();
for (int i = 0; i < size; i++)
mapping.put(buffer.readUniqueId(), (int) buffer.readShort());
}
@Override
public void write(PacketBuffer buffer) {
buffer.writeInt(entityID);
buffer.writeShort(mapping.size());
mapping.forEach((k, v) -> {
buffer.writeUniqueId(k);
buffer.writeShort(v);
});
}
@Override
public void handle(Supplier<Context> context) {
context.get()
.enqueueWork(() -> {
Entity entityByID = Minecraft.getInstance().world
.getEntityByID(entityID);
if (!(entityByID instanceof ContraptionEntity))
return;
ContraptionEntity contraptionEntity = (ContraptionEntity) entityByID;
contraptionEntity.contraption.seatMapping = mapping;
});
context.get()
.setPacketHandled(true);
}
}

View file

@ -47,10 +47,6 @@ public class ClockworkBearingTileEntity extends KineticTileEntity implements IBe
if (running && Contraption.isFrozen())
disassemble();
if (hourHand != null)
hourHand.collisionTick();
if (minuteHand != null)
minuteHand.collisionTick();
if (!world.isRemote && assembleNextTick) {
assembleNextTick = false;

View file

@ -208,8 +208,6 @@ public class MechanicalBearingTileEntity extends GeneratingKineticTileEntity imp
if (world.isRemote)
clientAngleDiff /= 2;
if (movedContraption != null)
movedContraption.collisionTick();
if (running && Contraption.isFrozen())
disassemble();

View file

@ -20,7 +20,6 @@ import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.Direction;
import net.minecraft.util.Direction.Axis;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.IWorld;
import net.minecraft.world.World;
import net.minecraft.world.gen.feature.template.Template.BlockInfo;
@ -91,13 +90,13 @@ public class MountedContraption extends Contraption {
}
@Override
public void removeBlocksFromWorld(IWorld world, BlockPos offset) {
super.removeBlocksFromWorld(world, offset, (pos, state) -> pos.equals(anchor));
protected boolean customBlockPlacement(IWorld world, BlockPos pos, BlockState state) {
return AllBlocks.MINECART_ANCHOR.has(state);
}
@Override
public void addBlocksToWorld(World world, BlockPos offset, Vec3d rotation) {
super.addBlocksToWorld(world, offset, rotation, (pos, state) -> AllBlocks.MINECART_ANCHOR.has(state));
protected boolean customBlockRemoval(IWorld world, BlockPos pos, BlockState state) {
return pos.equals(anchor);
}
}

View file

@ -53,7 +53,6 @@ public abstract class LinearActuatorTileEntity extends KineticTileEntity impleme
super.tick();
if (movedContraption != null) {
movedContraption.collisionTick();
if (!movedContraption.isAlive())
movedContraption = null;
}

View file

@ -16,7 +16,6 @@ import org.apache.commons.lang3.tuple.Pair;
import com.simibubi.create.content.contraptions.components.structureMovement.AllContraptionTypes;
import com.simibubi.create.content.contraptions.components.structureMovement.BlockMovementTraits;
import com.simibubi.create.content.contraptions.components.structureMovement.Contraption;
import com.simibubi.create.content.contraptions.components.structureMovement.glue.SuperGlueEntity;
import com.simibubi.create.content.contraptions.components.structureMovement.piston.MechanicalPistonBlock.PistonState;
import com.simibubi.create.foundation.config.AllConfigs;
import com.simibubi.create.foundation.utility.NBTHelper;
@ -32,7 +31,6 @@ import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.Direction;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.IWorld;
import net.minecraft.world.World;
import net.minecraft.world.gen.feature.template.Template.BlockInfo;
@ -174,44 +172,36 @@ public class PistonContraption extends Contraption {
}
@Override
public void addGlue(SuperGlueEntity entity) {
BlockPos pos = entity.getHangingPosition();
Direction direction = entity.getFacingDirection();
BlockPos localPos = pos.subtract(anchor)
public BlockPos toLocalPos(BlockPos globalPos) {
return globalPos.subtract(anchor)
.offset(orientation, -initialExtensionProgress);
this.superglue.add(Pair.of(localPos, direction));
glueToRemove.add(entity);
}
@Override
public void addBlocksToWorld(World world, BlockPos offset, Vec3d rotation) {
super.addBlocksToWorld(world, offset, rotation, (pos, state) -> {
BlockPos pistonPos = anchor.offset(orientation, -1);
BlockState pistonState = world.getBlockState(pistonPos);
TileEntity te = world.getTileEntity(pistonPos);
if (pos.equals(pistonPos)) {
if (te == null || te.isRemoved())
return true;
if (!isExtensionPole(state) && isPiston(pistonState))
world.setBlockState(pistonPos, pistonState.with(MechanicalPistonBlock.STATE, PistonState.RETRACTED),
3 | 16);
protected boolean customBlockPlacement(IWorld world, BlockPos pos, BlockState state) {
BlockPos pistonPos = anchor.offset(orientation, -1);
BlockState pistonState = world.getBlockState(pistonPos);
TileEntity te = world.getTileEntity(pistonPos);
if (pos.equals(pistonPos)) {
if (te == null || te.isRemoved())
return true;
}
return false;
});
if (!isExtensionPole(state) && isPiston(pistonState))
world.setBlockState(pistonPos, pistonState.with(MechanicalPistonBlock.STATE, PistonState.RETRACTED),
3 | 16);
return true;
}
return false;
}
@Override
public void removeBlocksFromWorld(IWorld world, BlockPos offset) {
super.removeBlocksFromWorld(world, offset, (pos, state) -> {
BlockPos pistonPos = anchor.offset(orientation, -1);
BlockState blockState = world.getBlockState(pos);
if (pos.equals(pistonPos) && isPiston(blockState)) {
world.setBlockState(pos, blockState.with(MechanicalPistonBlock.STATE, PistonState.MOVING), 66 | 16);
return true;
}
return false;
});
protected boolean customBlockRemoval(IWorld world, BlockPos pos, BlockState state) {
BlockPos pistonPos = anchor.offset(orientation, -1);
BlockState blockState = world.getBlockState(pos);
if (pos.equals(pistonPos) && isPiston(blockState)) {
world.setBlockState(pos, blockState.with(MechanicalPistonBlock.STATE, PistonState.MOVING), 66 | 16);
return true;
}
return false;
}
@Override

View file

@ -6,7 +6,6 @@ import com.simibubi.create.CreateClient;
import com.simibubi.create.foundation.collision.ContinuousOBBCollider.ContinuousSeparationManifold;
import com.simibubi.create.foundation.renderState.SuperRenderTypeBuffer;
import com.simibubi.create.foundation.utility.AngleHelper;
import com.simibubi.create.foundation.utility.Debug;
import com.simibubi.create.foundation.utility.MatrixStacker;
import com.simibubi.create.foundation.utility.outliner.AABBOutline;
@ -31,7 +30,6 @@ public class CollisionDebugger {
angle += delta;
angle = (int) angle;
OBB.setRotation(new Matrix3d().asZRotation(AngleHelper.rad(angle)));
Debug.debugMessage("Angle: " + angle);
}
public static void render(MatrixStack ms, SuperRenderTypeBuffer buffer) {

View file

@ -6,6 +6,8 @@ import java.util.function.Supplier;
import com.simibubi.create.Create;
import com.simibubi.create.content.contraptions.components.structureMovement.CancelPlayerFallPacket;
import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionInteractionPacket;
import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionSeatMappingPacket;
import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionStallPacket;
import com.simibubi.create.content.contraptions.components.structureMovement.glue.GlueEffectPacket;
import com.simibubi.create.content.contraptions.relays.advanced.sequencer.ConfigureSequencedGearshiftPacket;
@ -45,6 +47,7 @@ public enum AllPackets {
CONFIGURE_SCROLLABLE(ScrollValueUpdatePacket.class, ScrollValueUpdatePacket::new),
CANCEL_FALL(CancelPlayerFallPacket.class, CancelPlayerFallPacket::new),
EXTENDO_INTERACT(ExtendoGripInteractionPacket.class, ExtendoGripInteractionPacket::new),
CONTRAPTION_INTERACT(ContraptionInteractionPacket.class, ContraptionInteractionPacket::new),
PLACE_ARM(ArmPlacementPacket.class, ArmPlacementPacket::new),
// Server to Client
@ -54,6 +57,7 @@ public enum AllPackets {
CONFIGURE_CONFIG(ConfigureConfigPacket.class, ConfigureConfigPacket::new),
CONTRAPTION_STALL(ContraptionStallPacket.class, ContraptionStallPacket::new),
GLUE_EFFECT(GlueEffectPacket.class, GlueEffectPacket::new),
CONTRAPTION_SEAT_MAPPING(ContraptionSeatMappingPacket.class, ContraptionSeatMappingPacket::new),
;

View file

@ -13,6 +13,10 @@ import net.minecraft.util.math.Vec3i;
public class VecHelper {
public static Vec3d rotate(Vec3d vec, Vec3d rotationVec) {
return rotate(vec, rotationVec.x, rotationVec.y, rotationVec.z);
}
public static Vec3d rotate(Vec3d vec, double xRot, double yRot, double zRot) {
return rotate(rotate(rotate(vec, xRot, Axis.X), yRot, Axis.Y), zRot, Axis.Z);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 304 B

After

Width:  |  Height:  |  Size: 306 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 387 B

After

Width:  |  Height:  |  Size: 379 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 389 B

After

Width:  |  Height:  |  Size: 392 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 385 B

After

Width:  |  Height:  |  Size: 389 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 389 B

After

Width:  |  Height:  |  Size: 401 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 385 B

After

Width:  |  Height:  |  Size: 375 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 387 B

After

Width:  |  Height:  |  Size: 390 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 391 B

After

Width:  |  Height:  |  Size: 398 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 407 B

After

Width:  |  Height:  |  Size: 412 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 390 B

After

Width:  |  Height:  |  Size: 403 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 386 B

After

Width:  |  Height:  |  Size: 398 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 387 B

After

Width:  |  Height:  |  Size: 404 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 404 B

After

Width:  |  Height:  |  Size: 408 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 407 B

After

Width:  |  Height:  |  Size: 413 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 387 B

After

Width:  |  Height:  |  Size: 399 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 400 B

After

Width:  |  Height:  |  Size: 416 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 387 B

After

Width:  |  Height:  |  Size: 404 B