aboutsummaryrefslogtreecommitdiffhomepage
path: root/patch-remap/mache-vineflower/net/minecraft/world/entity/vehicle/AbstractMinecart.java.patch
diff options
context:
space:
mode:
Diffstat (limited to 'patch-remap/mache-vineflower/net/minecraft/world/entity/vehicle/AbstractMinecart.java.patch')
-rw-r--r--patch-remap/mache-vineflower/net/minecraft/world/entity/vehicle/AbstractMinecart.java.patch1292
1 files changed, 1292 insertions, 0 deletions
diff --git a/patch-remap/mache-vineflower/net/minecraft/world/entity/vehicle/AbstractMinecart.java.patch b/patch-remap/mache-vineflower/net/minecraft/world/entity/vehicle/AbstractMinecart.java.patch
new file mode 100644
index 0000000000..7653e3615f
--- /dev/null
+++ b/patch-remap/mache-vineflower/net/minecraft/world/entity/vehicle/AbstractMinecart.java.patch
@@ -0,0 +1,1292 @@
+--- a/net/minecraft/world/entity/vehicle/AbstractMinecart.java
++++ b/net/minecraft/world/entity/vehicle/AbstractMinecart.java
+@@ -3,8 +3,9 @@
+ import com.google.common.collect.ImmutableList;
+ import com.google.common.collect.ImmutableMap;
+ import com.google.common.collect.Maps;
++import com.google.common.collect.UnmodifiableIterator;
+ import com.mojang.datafixers.util.Pair;
+-import java.util.EnumMap;
++import java.util.Iterator;
+ import java.util.List;
+ import java.util.Map;
+ import javax.annotation.Nullable;
+@@ -24,15 +25,16 @@
+ import net.minecraft.util.Mth;
+ import net.minecraft.world.entity.Entity;
+ import net.minecraft.world.entity.EntityDimensions;
++import net.minecraft.world.entity.EntityPose;
+ import net.minecraft.world.entity.EntitySelector;
+ import net.minecraft.world.entity.EntityType;
++import net.minecraft.world.entity.EnumMoveType;
+ import net.minecraft.world.entity.LivingEntity;
+-import net.minecraft.world.entity.MoverType;
+-import net.minecraft.world.entity.Pose;
+ import net.minecraft.world.entity.animal.IronGolem;
+ import net.minecraft.world.entity.npc.Villager;
+ import net.minecraft.world.entity.npc.WanderingTrader;
+ import net.minecraft.world.entity.player.Player;
++import net.minecraft.world.item.Item;
+ import net.minecraft.world.item.ItemStack;
+ import net.minecraft.world.item.Items;
+ import net.minecraft.world.level.Level;
+@@ -40,21 +42,28 @@
+ import net.minecraft.world.level.block.Block;
+ import net.minecraft.world.level.block.Blocks;
+ import net.minecraft.world.level.block.PoweredRailBlock;
+-import net.minecraft.world.level.block.state.BlockState;
++import net.minecraft.world.level.block.state.IBlockData;
+ import net.minecraft.world.level.block.state.properties.RailShape;
+ import net.minecraft.world.phys.AABB;
+ import net.minecraft.world.phys.Vec3;
+ import org.joml.Vector3f;
+
++// CraftBukkit start
++import org.bukkit.Location;
++import org.bukkit.craftbukkit.util.CraftLocation;
++import org.bukkit.entity.Vehicle;
++import org.bukkit.event.vehicle.VehicleEntityCollisionEvent;
++import org.bukkit.util.Vector;
++// CraftBukkit end
++
+ public abstract class AbstractMinecart extends VehicleEntity {
++
+ private static final float LOWERED_PASSENGER_ATTACHMENT_Y = 0.0F;
+ private static final float PASSENGER_ATTACHMENT_Y = 0.1875F;
+ private static final EntityDataAccessor<Integer> DATA_ID_DISPLAY_BLOCK = SynchedEntityData.defineId(AbstractMinecart.class, EntityDataSerializers.INT);
+ private static final EntityDataAccessor<Integer> DATA_ID_DISPLAY_OFFSET = SynchedEntityData.defineId(AbstractMinecart.class, EntityDataSerializers.INT);
+ private static final EntityDataAccessor<Boolean> DATA_ID_CUSTOM_DISPLAY = SynchedEntityData.defineId(AbstractMinecart.class, EntityDataSerializers.BOOLEAN);
+- private static final ImmutableMap<Pose, ImmutableList<Integer>> POSE_DISMOUNT_HEIGHTS = ImmutableMap.of(
+- Pose.STANDING, ImmutableList.of(0, 1, -1), Pose.CROUCHING, ImmutableList.of(0, 1, -1), Pose.SWIMMING, ImmutableList.of(0, 1)
+- );
++ private static final ImmutableMap<EntityPose, ImmutableList<Integer>> POSE_DISMOUNT_HEIGHTS = ImmutableMap.of(EntityPose.STANDING, ImmutableList.of(0, 1, -1), EntityPose.CROUCHING, ImmutableList.of(0, 1, -1), EntityPose.SWIMMING, ImmutableList.of(0, 1));
+ protected static final float WATER_SLOWDOWN_FACTOR = 0.95F;
+ private boolean flipped;
+ private boolean onRails;
+@@ -64,55 +73,84 @@
+ private double lerpZ;
+ private double lerpYRot;
+ private double lerpXRot;
+- private Vec3 targetDeltaMovement = Vec3.ZERO;
+- private static final Map<RailShape, Pair<Vec3i, Vec3i>> EXITS = Util.make(Maps.newEnumMap(RailShape.class), map -> {
+- Vec3i normal = Direction.WEST.getNormal();
+- Vec3i normal1 = Direction.EAST.getNormal();
+- Vec3i normal2 = Direction.NORTH.getNormal();
+- Vec3i normal3 = Direction.SOUTH.getNormal();
+- Vec3i vec3i = normal.below();
+- Vec3i vec3i1 = normal1.below();
+- Vec3i vec3i2 = normal2.below();
+- Vec3i vec3i3 = normal3.below();
+- map.put(RailShape.NORTH_SOUTH, Pair.of(normal2, normal3));
+- map.put(RailShape.EAST_WEST, Pair.of(normal, normal1));
+- map.put(RailShape.ASCENDING_EAST, Pair.of(vec3i, normal1));
+- map.put(RailShape.ASCENDING_WEST, Pair.of(normal, vec3i1));
+- map.put(RailShape.ASCENDING_NORTH, Pair.of(normal2, vec3i3));
+- map.put(RailShape.ASCENDING_SOUTH, Pair.of(vec3i2, normal3));
+- map.put(RailShape.SOUTH_EAST, Pair.of(normal3, normal1));
+- map.put(RailShape.SOUTH_WEST, Pair.of(normal3, normal));
+- map.put(RailShape.NORTH_WEST, Pair.of(normal2, normal));
+- map.put(RailShape.NORTH_EAST, Pair.of(normal2, normal1));
++ private Vec3 targetDeltaMovement;
++ private static final Map<RailShape, Pair<Vec3i, Vec3i>> EXITS = (Map) Util.make(Maps.newEnumMap(RailShape.class), (enummap) -> {
++ Vec3i baseblockposition = Direction.WEST.getNormal();
++ Vec3i baseblockposition1 = Direction.EAST.getNormal();
++ Vec3i baseblockposition2 = Direction.NORTH.getNormal();
++ Vec3i baseblockposition3 = Direction.SOUTH.getNormal();
++ Vec3i baseblockposition4 = baseblockposition.below();
++ Vec3i baseblockposition5 = baseblockposition1.below();
++ Vec3i baseblockposition6 = baseblockposition2.below();
++ Vec3i baseblockposition7 = baseblockposition3.below();
++
++ enummap.put(RailShape.NORTH_SOUTH, Pair.of(baseblockposition2, baseblockposition3));
++ enummap.put(RailShape.EAST_WEST, Pair.of(baseblockposition, baseblockposition1));
++ enummap.put(RailShape.ASCENDING_EAST, Pair.of(baseblockposition4, baseblockposition1));
++ enummap.put(RailShape.ASCENDING_WEST, Pair.of(baseblockposition, baseblockposition5));
++ enummap.put(RailShape.ASCENDING_NORTH, Pair.of(baseblockposition2, baseblockposition7));
++ enummap.put(RailShape.ASCENDING_SOUTH, Pair.of(baseblockposition6, baseblockposition3));
++ enummap.put(RailShape.SOUTH_EAST, Pair.of(baseblockposition3, baseblockposition1));
++ enummap.put(RailShape.SOUTH_WEST, Pair.of(baseblockposition3, baseblockposition));
++ enummap.put(RailShape.NORTH_WEST, Pair.of(baseblockposition2, baseblockposition));
++ enummap.put(RailShape.NORTH_EAST, Pair.of(baseblockposition2, baseblockposition1));
+ });
+
++ // CraftBukkit start
++ public boolean slowWhenEmpty = true;
++ private double derailedX = 0.5;
++ private double derailedY = 0.5;
++ private double derailedZ = 0.5;
++ private double flyingX = 0.95;
++ private double flyingY = 0.95;
++ private double flyingZ = 0.95;
++ public double maxSpeed = 0.4D;
++ // CraftBukkit end
++
+ protected AbstractMinecart(EntityType<?> entityType, Level level) {
+ super(entityType, level);
++ this.targetDeltaMovement = Vec3.ZERO;
+ this.blocksBuilding = true;
+ }
+
+- protected AbstractMinecart(EntityType<?> entityType, Level level, double x, double y, double z) {
++ protected AbstractMinecart(EntityType<?> entityType, Level level, double x, double d1, double y) {
+ this(entityType, level);
+- this.setPos(x, y, z);
++ this.setPos(x, d1, y);
+ this.xo = x;
+- this.yo = y;
+- this.zo = z;
++ this.yo = d1;
++ this.zo = y;
+ }
+
+- public static AbstractMinecart createMinecart(
+- ServerLevel serverLevel, double d, double d1, double d2, AbstractMinecart.Type type, ItemStack itemStack, @Nullable Player player
+- ) {
+- AbstractMinecart abstractMinecart = (AbstractMinecart)(switch (type) {
+- case CHEST -> new MinecartChest(serverLevel, d, d1, d2);
+- case FURNACE -> new MinecartFurnace(serverLevel, d, d1, d2);
+- case TNT -> new MinecartTNT(serverLevel, d, d1, d2);
+- case SPAWNER -> new MinecartSpawner(serverLevel, d, d1, d2);
+- case HOPPER -> new MinecartHopper(serverLevel, d, d1, d2);
+- case COMMAND_BLOCK -> new MinecartCommandBlock(serverLevel, d, d1, d2);
+- default -> new Minecart(serverLevel, d, d1, d2);
+- });
+- EntityType.<AbstractMinecart>createDefaultStackConfig(serverLevel, itemStack, player).accept(abstractMinecart);
+- return abstractMinecart;
++ public static AbstractMinecart createMinecart(ServerLevel worldserver, double d0, double d1, double d2, AbstractMinecart.EnumMinecartType entityminecartabstract_enumminecarttype, ItemStack itemstack, @Nullable Player entityhuman) {
++ Object object;
++
++ switch (entityminecartabstract_enumminecarttype) {
++ case CHEST:
++ object = new MinecartChest(worldserver, d0, d1, d2);
++ break;
++ case FURNACE:
++ object = new MinecartFurnace(worldserver, d0, d1, d2);
++ break;
++ case TNT:
++ object = new MinecartTNT(worldserver, d0, d1, d2);
++ break;
++ case SPAWNER:
++ object = new MinecartSpawner(worldserver, d0, d1, d2);
++ break;
++ case HOPPER:
++ object = new MinecartHopper(worldserver, d0, d1, d2);
++ break;
++ case COMMAND_BLOCK:
++ object = new MinecartCommandBlock(worldserver, d0, d1, d2);
++ break;
++ default:
++ object = new Minecart(worldserver, d0, d1, d2);
++ }
++
++ AbstractMinecart object1 = (AbstractMinecart) object; // CraftBukkit- decompile error
++
++ EntityType.createDefaultStackConfig(worldserver, itemstack, entityhuman).accept(object1);
++ return (AbstractMinecart) object1;
+ }
+
+ @Override
+@@ -123,9 +161,9 @@
+ @Override
+ protected void defineSynchedData() {
+ super.defineSynchedData();
+- this.entityData.define(DATA_ID_DISPLAY_BLOCK, Block.getId(Blocks.AIR.defaultBlockState()));
+- this.entityData.define(DATA_ID_DISPLAY_OFFSET, 6);
+- this.entityData.define(DATA_ID_CUSTOM_DISPLAY, false);
++ this.entityData.define(AbstractMinecart.DATA_ID_DISPLAY_BLOCK, Block.getId(Blocks.AIR.defaultBlockState()));
++ this.entityData.define(AbstractMinecart.DATA_ID_DISPLAY_OFFSET, 6);
++ this.entityData.define(AbstractMinecart.DATA_ID_CUSTOM_DISPLAY, false);
+ }
+
+ @Override
+@@ -144,55 +182,72 @@
+ }
+
+ @Override
+- protected Vector3f getPassengerAttachmentPoint(Entity entity, EntityDimensions entityDimensions, float f) {
++ protected Vector3f getPassengerAttachmentPoint(Entity entity, EntityDimensions entitysize, float f) {
+ boolean flag = entity instanceof Villager || entity instanceof WanderingTrader;
++
+ return new Vector3f(0.0F, flag ? 0.0F : 0.1875F, 0.0F);
+ }
+
+ @Override
+ public Vec3 getDismountLocationForPassenger(LivingEntity livingEntity) {
+- Direction motionDirection = this.getMotionDirection();
+- if (motionDirection.getAxis() == Direction.Axis.Y) {
++ Direction enumdirection = this.getMotionDirection();
++
++ if (enumdirection.getAxis() == Direction.Axis.Y) {
+ return super.getDismountLocationForPassenger(livingEntity);
+ } else {
+- int[][] ints = DismountHelper.offsetsForDirection(motionDirection);
+- BlockPos blockPos = this.blockPosition();
+- BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos();
+- ImmutableList<Pose> dismountPoses = livingEntity.getDismountPoses();
++ int[][] aint = DismountHelper.offsetsForDirection(enumdirection);
++ BlockPos blockposition = this.blockPosition();
++ BlockPos.MutableBlockPos blockposition_mutableblockposition = new BlockPos.MutableBlockPos();
++ ImmutableList<EntityPose> immutablelist = livingEntity.getDismountPoses();
++ UnmodifiableIterator unmodifiableiterator = immutablelist.iterator();
+
+- for (Pose pose : dismountPoses) {
+- EntityDimensions dimensions = livingEntity.getDimensions(pose);
+- float f = Math.min(dimensions.width, 1.0F) / 2.0F;
++ while (unmodifiableiterator.hasNext()) {
++ EntityPose entitypose = (EntityPose) unmodifiableiterator.next();
++ EntityDimensions entitysize = livingEntity.getDimensions(entitypose);
++ float f = Math.min(entitysize.width, 1.0F) / 2.0F;
++ UnmodifiableIterator unmodifiableiterator1 = ((ImmutableList) AbstractMinecart.POSE_DISMOUNT_HEIGHTS.get(entitypose)).iterator();
+
+- for (int i : POSE_DISMOUNT_HEIGHTS.get(pose)) {
+- for (int[] ints1 : ints) {
+- mutableBlockPos.set(blockPos.getX() + ints1[0], blockPos.getY() + i, blockPos.getZ() + ints1[1]);
+- double blockFloorHeight = this.level()
+- .getBlockFloorHeight(
+- DismountHelper.nonClimbableShape(this.level(), mutableBlockPos),
+- () -> DismountHelper.nonClimbableShape(this.level(), mutableBlockPos.below())
+- );
+- if (DismountHelper.isBlockFloorValid(blockFloorHeight)) {
+- AABB aABB = new AABB((double)(-f), 0.0, (double)(-f), (double)f, (double)dimensions.height, (double)f);
+- Vec3 vec3 = Vec3.upFromBottomCenterOf(mutableBlockPos, blockFloorHeight);
+- if (DismountHelper.canDismountTo(this.level(), livingEntity, aABB.move(vec3))) {
+- livingEntity.setPose(pose);
+- return vec3;
++ while (unmodifiableiterator1.hasNext()) {
++ int i = (Integer) unmodifiableiterator1.next();
++ int[][] aint1 = aint;
++ int j = aint.length;
++
++ for (int k = 0; k < j; ++k) {
++ int[] aint2 = aint1[k];
++
++ blockposition_mutableblockposition.set(blockposition.getX() + aint2[0], blockposition.getY() + i, blockposition.getZ() + aint2[1]);
++ double d0 = this.level().getBlockFloorHeight(DismountHelper.nonClimbableShape(this.level(), blockposition_mutableblockposition), () -> {
++ return DismountHelper.nonClimbableShape(this.level(), blockposition_mutableblockposition.below());
++ });
++
++ if (DismountHelper.isBlockFloorValid(d0)) {
++ AABB axisalignedbb = new AABB((double) (-f), 0.0D, (double) (-f), (double) f, (double) entitysize.height, (double) f);
++ Vec3 vec3d = Vec3.upFromBottomCenterOf(blockposition_mutableblockposition, d0);
++
++ if (DismountHelper.canDismountTo(this.level(), livingEntity, axisalignedbb.move(vec3d))) {
++ livingEntity.setPose(entitypose);
++ return vec3d;
+ }
+ }
+ }
+ }
+ }
+
+- double d = this.getBoundingBox().maxY;
+- mutableBlockPos.set((double)blockPos.getX(), d, (double)blockPos.getZ());
++ double d1 = this.getBoundingBox().maxY;
+
+- for (Pose pose1 : dismountPoses) {
+- double d1 = (double)livingEntity.getDimensions(pose1).height;
+- int ceil = Mth.ceil(d - (double)mutableBlockPos.getY() + d1);
+- double d2 = DismountHelper.findCeilingFrom(mutableBlockPos, ceil, pos -> this.level().getBlockState(pos).getCollisionShape(this.level(), pos));
+- if (d + d1 <= d2) {
+- livingEntity.setPose(pose1);
++ blockposition_mutableblockposition.set((double) blockposition.getX(), d1, (double) blockposition.getZ());
++ UnmodifiableIterator unmodifiableiterator2 = immutablelist.iterator();
++
++ while (unmodifiableiterator2.hasNext()) {
++ EntityPose entitypose1 = (EntityPose) unmodifiableiterator2.next();
++ double d2 = (double) livingEntity.getDimensions(entitypose1).height;
++ int l = Mth.ceil(d1 - (double) blockposition_mutableblockposition.getY() + d2);
++ double d3 = DismountHelper.findCeilingFrom(blockposition_mutableblockposition, l, (blockposition1) -> {
++ return this.level().getBlockState(blockposition1).getCollisionShape(this.level(), blockposition1);
++ });
++
++ if (d1 + d2 <= d3) {
++ livingEntity.setPose(entitypose1);
+ break;
+ }
+ }
+@@ -203,8 +258,9 @@
+
+ @Override
+ protected float getBlockSpeedFactor() {
+- BlockState blockState = this.level().getBlockState(this.blockPosition());
+- return blockState.is(BlockTags.RAILS) ? 1.0F : super.getBlockSpeedFactor();
++ IBlockData iblockdata = this.level().getBlockState(this.blockPosition());
++
++ return iblockdata.is(BlockTags.RAILS) ? 1.0F : super.getBlockSpeedFactor();
+ }
+
+ @Override
+@@ -220,7 +276,7 @@
+ }
+
+ private static Pair<Vec3i, Vec3i> exits(RailShape shape) {
+- return EXITS.get(shape);
++ return (Pair) AbstractMinecart.EXITS.get(shape);
+ }
+
+ @Override
+@@ -230,6 +286,14 @@
+
+ @Override
+ public void tick() {
++ // CraftBukkit start
++ double prevX = this.getX();
++ double prevY = this.getY();
++ double prevZ = this.getZ();
++ float prevYaw = this.getYRot();
++ float prevPitch = this.getXRot();
++ // CraftBukkit end
++
+ if (this.getHurtTime() > 0) {
+ this.setHurtTime(this.getHurtTime() - 1);
+ }
+@@ -239,35 +303,39 @@
+ }
+
+ this.checkBelowWorld();
+- this.handleNetherPortal();
++ // this.handleNetherPortal(); // CraftBukkit - handled in postTick
+ if (this.level().isClientSide) {
+ if (this.lerpSteps > 0) {
+ this.lerpPositionAndRotationStep(this.lerpSteps, this.lerpX, this.lerpY, this.lerpZ, this.lerpYRot, this.lerpXRot);
+- this.lerpSteps--;
++ --this.lerpSteps;
+ } else {
+ this.reapplyPosition();
+ this.setRot(this.getYRot(), this.getXRot());
+ }
++
+ } else {
+ if (!this.isNoGravity()) {
+- double d = this.isInWater() ? -0.005 : -0.04;
+- this.setDeltaMovement(this.getDeltaMovement().add(0.0, d, 0.0));
++ double d0 = this.isInWater() ? -0.005D : -0.04D;
++
++ this.setDeltaMovement(this.getDeltaMovement().add(0.0D, d0, 0.0D));
+ }
+
+- int floor = Mth.floor(this.getX());
+- int floor1 = Mth.floor(this.getY());
+- int floor2 = Mth.floor(this.getZ());
+- if (this.level().getBlockState(new BlockPos(floor, floor1 - 1, floor2)).is(BlockTags.RAILS)) {
+- floor1--;
++ int i = Mth.floor(this.getX());
++ int j = Mth.floor(this.getY());
++ int k = Mth.floor(this.getZ());
++
++ if (this.level().getBlockState(new BlockPos(i, j - 1, k)).is(BlockTags.RAILS)) {
++ --j;
+ }
+
+- BlockPos blockPos = new BlockPos(floor, floor1, floor2);
+- BlockState blockState = this.level().getBlockState(blockPos);
+- this.onRails = BaseRailBlock.isRail(blockState);
++ BlockPos blockposition = new BlockPos(i, j, k);
++ IBlockData iblockdata = this.level().getBlockState(blockposition);
++
++ this.onRails = BaseRailBlock.isRail(iblockdata);
+ if (this.onRails) {
+- this.moveAlongTrack(blockPos, blockState);
+- if (blockState.is(Blocks.ACTIVATOR_RAIL)) {
+- this.activateMinecart(floor, floor1, floor2, blockState.getValue(PoweredRailBlock.POWERED));
++ this.moveAlongTrack(blockposition, iblockdata);
++ if (iblockdata.is(Blocks.ACTIVATOR_RAIL)) {
++ this.activateMinecart(i, j, k, (Boolean) iblockdata.getValue(PoweredRailBlock.POWERED));
+ }
+ } else {
+ this.comeOffTrack();
+@@ -277,38 +345,83 @@
+ this.setXRot(0.0F);
+ double d1 = this.xo - this.getX();
+ double d2 = this.zo - this.getZ();
+- if (d1 * d1 + d2 * d2 > 0.001) {
+- this.setYRot((float)(Mth.atan2(d2, d1) * 180.0 / Math.PI));
++
++ if (d1 * d1 + d2 * d2 > 0.001D) {
++ this.setYRot((float) (Mth.atan2(d2, d1) * 180.0D / 3.141592653589793D));
+ if (this.flipped) {
+ this.setYRot(this.getYRot() + 180.0F);
+ }
+ }
+
+- double d3 = (double)Mth.wrapDegrees(this.getYRot() - this.yRotO);
+- if (d3 < -170.0 || d3 >= 170.0) {
++ double d3 = (double) Mth.wrapDegrees(this.getYRot() - this.yRotO);
++
++ if (d3 < -170.0D || d3 >= 170.0D) {
+ this.setYRot(this.getYRot() + 180.0F);
+ this.flipped = !this.flipped;
+ }
+
+ this.setRot(this.getYRot(), this.getXRot());
+- if (this.getMinecartType() == AbstractMinecart.Type.RIDEABLE && this.getDeltaMovement().horizontalDistanceSqr() > 0.01) {
+- List<Entity> entities = this.level().getEntities(this, this.getBoundingBox().inflate(0.2F, 0.0, 0.2F), EntitySelector.pushableBy(this));
+- if (!entities.isEmpty()) {
+- for (Entity entity : entities) {
+- if (!(entity instanceof Player)
+- && !(entity instanceof IronGolem)
+- && !(entity instanceof AbstractMinecart)
+- && !this.isVehicle()
+- && !entity.isPassenger()) {
++ // CraftBukkit start
++ org.bukkit.World bworld = this.level().getWorld();
++ Location from = new Location(bworld, prevX, prevY, prevZ, prevYaw, prevPitch);
++ Location to = CraftLocation.toBukkit(this.position(), bworld, this.getYRot(), this.getXRot());
++ Vehicle vehicle = (Vehicle) this.getBukkitEntity();
++
++ this.level().getCraftServer().getPluginManager().callEvent(new org.bukkit.event.vehicle.VehicleUpdateEvent(vehicle));
++
++ if (!from.equals(to)) {
++ this.level().getCraftServer().getPluginManager().callEvent(new org.bukkit.event.vehicle.VehicleMoveEvent(vehicle, from, to));
++ }
++ // CraftBukkit end
++ if (this.getMinecartType() == AbstractMinecart.EnumMinecartType.RIDEABLE && this.getDeltaMovement().horizontalDistanceSqr() > 0.01D) {
++ List<Entity> list = this.level().getEntities((Entity) this, this.getBoundingBox().inflate(0.20000000298023224D, 0.0D, 0.20000000298023224D), EntitySelector.pushableBy(this));
++
++ if (!list.isEmpty()) {
++ Iterator iterator = list.iterator();
++
++ while (iterator.hasNext()) {
++ Entity entity = (Entity) iterator.next();
++
++ if (!(entity instanceof Player) && !(entity instanceof IronGolem) && !(entity instanceof AbstractMinecart) && !this.isVehicle() && !entity.isPassenger()) {
++ // CraftBukkit start
++ VehicleEntityCollisionEvent collisionEvent = new VehicleEntityCollisionEvent(vehicle, entity.getBukkitEntity());
++ this.level().getCraftServer().getPluginManager().callEvent(collisionEvent);
++
++ if (collisionEvent.isCancelled()) {
++ continue;
++ }
++ // CraftBukkit end
+ entity.startRiding(this);
+ } else {
++ // CraftBukkit start
++ if (!this.isPassengerOfSameVehicle(entity)) {
++ VehicleEntityCollisionEvent collisionEvent = new VehicleEntityCollisionEvent(vehicle, entity.getBukkitEntity());
++ this.level().getCraftServer().getPluginManager().callEvent(collisionEvent);
++
++ if (collisionEvent.isCancelled()) {
++ continue;
++ }
++ }
++ // CraftBukkit end
+ entity.push(this);
+ }
+ }
+ }
+ } else {
+- for (Entity entity1 : this.level().getEntities(this, this.getBoundingBox().inflate(0.2F, 0.0, 0.2F))) {
++ Iterator iterator1 = this.level().getEntities(this, this.getBoundingBox().inflate(0.20000000298023224D, 0.0D, 0.20000000298023224D)).iterator();
++
++ while (iterator1.hasNext()) {
++ Entity entity1 = (Entity) iterator1.next();
++
+ if (!this.hasPassenger(entity1) && entity1.isPushable() && entity1 instanceof AbstractMinecart) {
++ // CraftBukkit start
++ VehicleEntityCollisionEvent collisionEvent = new VehicleEntityCollisionEvent(vehicle, entity1.getBukkitEntity());
++ this.level().getCraftServer().getPluginManager().callEvent(collisionEvent);
++
++ if (collisionEvent.isCancelled()) {
++ continue;
++ }
++ // CraftBukkit end
+ entity1.push(this);
+ }
+ }
+@@ -325,183 +438,210 @@
+ }
+
+ protected double getMaxSpeed() {
+- return (this.isInWater() ? 4.0 : 8.0) / 20.0;
++ return (this.isInWater() ? this.maxSpeed / 2.0D: this.maxSpeed); // CraftBukkit
+ }
+
+- public void activateMinecart(int x, int y, int z, boolean powered) {
+- }
++ public void activateMinecart(int x, int y, int z, boolean powered) {}
+
+ protected void comeOffTrack() {
+- double maxSpeed = this.getMaxSpeed();
+- Vec3 deltaMovement = this.getDeltaMovement();
+- this.setDeltaMovement(Mth.clamp(deltaMovement.x, -maxSpeed, maxSpeed), deltaMovement.y, Mth.clamp(deltaMovement.z, -maxSpeed, maxSpeed));
++ double d0 = this.getMaxSpeed();
++ Vec3 vec3d = this.getDeltaMovement();
++
++ this.setDeltaMovement(Mth.clamp(vec3d.x, -d0, d0), vec3d.y, Mth.clamp(vec3d.z, -d0, d0));
+ if (this.onGround()) {
+- this.setDeltaMovement(this.getDeltaMovement().scale(0.5));
++ // CraftBukkit start - replace magic numbers with our variables
++ this.setDeltaMovement(new Vec3(this.getDeltaMovement().x * this.derailedX, this.getDeltaMovement().y * this.derailedY, this.getDeltaMovement().z * this.derailedZ));
++ // CraftBukkit end
+ }
+
+- this.move(MoverType.SELF, this.getDeltaMovement());
++ this.move(EnumMoveType.SELF, this.getDeltaMovement());
+ if (!this.onGround()) {
+- this.setDeltaMovement(this.getDeltaMovement().scale(0.95));
++ // CraftBukkit start - replace magic numbers with our variables
++ this.setDeltaMovement(new Vec3(this.getDeltaMovement().x * this.flyingX, this.getDeltaMovement().y * this.flyingY, this.getDeltaMovement().z * this.flyingZ));
++ // CraftBukkit end
+ }
++
+ }
+
+- protected void moveAlongTrack(BlockPos pos, BlockState state) {
++ protected void moveAlongTrack(BlockPos pos, IBlockData state) {
+ this.resetFallDistance();
+- double x = this.getX();
+- double y = this.getY();
+- double z = this.getZ();
+- Vec3 pos1 = this.getPos(x, y, z);
+- double var56 = (double)pos.getY();
++ double d0 = this.getX();
++ double d1 = this.getY();
++ double d2 = this.getZ();
++ Vec3 vec3d = this.getPos(d0, d1, d2);
++
++ d1 = (double) pos.getY();
+ boolean flag = false;
+ boolean flag1 = false;
++
+ if (state.is(Blocks.POWERED_RAIL)) {
+- flag = state.getValue(PoweredRailBlock.POWERED);
++ flag = (Boolean) state.getValue(PoweredRailBlock.POWERED);
+ flag1 = !flag;
+ }
+
+- double d = 0.0078125;
++ double d3 = 0.0078125D;
++
+ if (this.isInWater()) {
+- d *= 0.2;
++ d3 *= 0.2D;
+ }
+
+- Vec3 deltaMovement = this.getDeltaMovement();
+- RailShape railShape = state.getValue(((BaseRailBlock)state.getBlock()).getShapeProperty());
+- switch (railShape) {
++ Vec3 vec3d1 = this.getDeltaMovement();
++ RailShape blockpropertytrackposition = (RailShape) state.getValue(((BaseRailBlock) state.getBlock()).getShapeProperty());
++
++ switch (blockpropertytrackposition) {
+ case ASCENDING_EAST:
+- this.setDeltaMovement(deltaMovement.add(-d, 0.0, 0.0));
+- var56++;
++ this.setDeltaMovement(vec3d1.add(-d3, 0.0D, 0.0D));
++ ++d1;
+ break;
+ case ASCENDING_WEST:
+- this.setDeltaMovement(deltaMovement.add(d, 0.0, 0.0));
+- var56++;
++ this.setDeltaMovement(vec3d1.add(d3, 0.0D, 0.0D));
++ ++d1;
+ break;
+ case ASCENDING_NORTH:
+- this.setDeltaMovement(deltaMovement.add(0.0, 0.0, d));
+- var56++;
++ this.setDeltaMovement(vec3d1.add(0.0D, 0.0D, d3));
++ ++d1;
+ break;
+ case ASCENDING_SOUTH:
+- this.setDeltaMovement(deltaMovement.add(0.0, 0.0, -d));
+- var56++;
++ this.setDeltaMovement(vec3d1.add(0.0D, 0.0D, -d3));
++ ++d1;
+ }
+
+- deltaMovement = this.getDeltaMovement();
+- Pair<Vec3i, Vec3i> pair = exits(railShape);
+- Vec3i vec3i = pair.getFirst();
+- Vec3i vec3i1 = pair.getSecond();
+- double d1 = (double)(vec3i1.getX() - vec3i.getX());
+- double d2 = (double)(vec3i1.getZ() - vec3i.getZ());
+- double squareRoot = Math.sqrt(d1 * d1 + d2 * d2);
+- double d3 = deltaMovement.x * d1 + deltaMovement.z * d2;
+- if (d3 < 0.0) {
+- d1 = -d1;
+- d2 = -d2;
++ vec3d1 = this.getDeltaMovement();
++ Pair<Vec3i, Vec3i> pair = exits(blockpropertytrackposition);
++ Vec3i baseblockposition = (Vec3i) pair.getFirst();
++ Vec3i baseblockposition1 = (Vec3i) pair.getSecond();
++ double d4 = (double) (baseblockposition1.getX() - baseblockposition.getX());
++ double d5 = (double) (baseblockposition1.getZ() - baseblockposition.getZ());
++ double d6 = Math.sqrt(d4 * d4 + d5 * d5);
++ double d7 = vec3d1.x * d4 + vec3d1.z * d5;
++
++ if (d7 < 0.0D) {
++ d4 = -d4;
++ d5 = -d5;
+ }
+
+- double min = Math.min(2.0, deltaMovement.horizontalDistance());
+- deltaMovement = new Vec3(min * d1 / squareRoot, deltaMovement.y, min * d2 / squareRoot);
+- this.setDeltaMovement(deltaMovement);
+- Entity firstPassenger = this.getFirstPassenger();
+- if (firstPassenger instanceof Player) {
+- Vec3 deltaMovement1 = firstPassenger.getDeltaMovement();
+- double d4 = deltaMovement1.horizontalDistanceSqr();
+- double d5 = this.getDeltaMovement().horizontalDistanceSqr();
+- if (d4 > 1.0E-4 && d5 < 0.01) {
+- this.setDeltaMovement(this.getDeltaMovement().add(deltaMovement1.x * 0.1, 0.0, deltaMovement1.z * 0.1));
++ double d8 = Math.min(2.0D, vec3d1.horizontalDistance());
++
++ vec3d1 = new Vec3(d8 * d4 / d6, vec3d1.y, d8 * d5 / d6);
++ this.setDeltaMovement(vec3d1);
++ Entity entity = this.getFirstPassenger();
++
++ if (entity instanceof Player) {
++ Vec3 vec3d2 = entity.getDeltaMovement();
++ double d9 = vec3d2.horizontalDistanceSqr();
++ double d10 = this.getDeltaMovement().horizontalDistanceSqr();
++
++ if (d9 > 1.0E-4D && d10 < 0.01D) {
++ this.setDeltaMovement(this.getDeltaMovement().add(vec3d2.x * 0.1D, 0.0D, vec3d2.z * 0.1D));
+ flag1 = false;
+ }
+ }
+
++ double d11;
++
+ if (flag1) {
+- double d6 = this.getDeltaMovement().horizontalDistance();
+- if (d6 < 0.03) {
++ d11 = this.getDeltaMovement().horizontalDistance();
++ if (d11 < 0.03D) {
+ this.setDeltaMovement(Vec3.ZERO);
+ } else {
+- this.setDeltaMovement(this.getDeltaMovement().multiply(0.5, 0.0, 0.5));
++ this.setDeltaMovement(this.getDeltaMovement().multiply(0.5D, 0.0D, 0.5D));
+ }
+ }
+
+- double d6 = (double)pos.getX() + 0.5 + (double)vec3i.getX() * 0.5;
+- double d7 = (double)pos.getZ() + 0.5 + (double)vec3i.getZ() * 0.5;
+- double d8 = (double)pos.getX() + 0.5 + (double)vec3i1.getX() * 0.5;
+- double d9 = (double)pos.getZ() + 0.5 + (double)vec3i1.getZ() * 0.5;
+- d1 = d8 - d6;
+- d2 = d9 - d7;
+- double d10;
+- if (d1 == 0.0) {
+- d10 = z - (double)pos.getZ();
+- } else if (d2 == 0.0) {
+- d10 = x - (double)pos.getX();
++ d11 = (double) pos.getX() + 0.5D + (double) baseblockposition.getX() * 0.5D;
++ double d12 = (double) pos.getZ() + 0.5D + (double) baseblockposition.getZ() * 0.5D;
++ double d13 = (double) pos.getX() + 0.5D + (double) baseblockposition1.getX() * 0.5D;
++ double d14 = (double) pos.getZ() + 0.5D + (double) baseblockposition1.getZ() * 0.5D;
++
++ d4 = d13 - d11;
++ d5 = d14 - d12;
++ double d15;
++ double d16;
++ double d17;
++
++ if (d4 == 0.0D) {
++ d15 = d2 - (double) pos.getZ();
++ } else if (d5 == 0.0D) {
++ d15 = d0 - (double) pos.getX();
+ } else {
+- double d11 = x - d6;
+- double d12 = z - d7;
+- d10 = (d11 * d1 + d12 * d2) * 2.0;
++ d16 = d0 - d11;
++ d17 = d2 - d12;
++ d15 = (d16 * d4 + d17 * d5) * 2.0D;
+ }
+
+- x = d6 + d1 * d10;
+- z = d7 + d2 * d10;
+- this.setPos(x, var56, z);
+- double d11 = this.isVehicle() ? 0.75 : 1.0;
+- double d12 = this.getMaxSpeed();
+- deltaMovement = this.getDeltaMovement();
+- this.move(MoverType.SELF, new Vec3(Mth.clamp(d11 * deltaMovement.x, -d12, d12), 0.0, Mth.clamp(d11 * deltaMovement.z, -d12, d12)));
+- if (vec3i.getY() != 0 && Mth.floor(this.getX()) - pos.getX() == vec3i.getX() && Mth.floor(this.getZ()) - pos.getZ() == vec3i.getZ()) {
+- this.setPos(this.getX(), this.getY() + (double)vec3i.getY(), this.getZ());
+- } else if (vec3i1.getY() != 0 && Mth.floor(this.getX()) - pos.getX() == vec3i1.getX() && Mth.floor(this.getZ()) - pos.getZ() == vec3i1.getZ()) {
+- this.setPos(this.getX(), this.getY() + (double)vec3i1.getY(), this.getZ());
++ d0 = d11 + d4 * d15;
++ d2 = d12 + d5 * d15;
++ this.setPos(d0, d1, d2);
++ d16 = this.isVehicle() ? 0.75D : 1.0D;
++ d17 = this.getMaxSpeed();
++ vec3d1 = this.getDeltaMovement();
++ this.move(EnumMoveType.SELF, new Vec3(Mth.clamp(d16 * vec3d1.x, -d17, d17), 0.0D, Mth.clamp(d16 * vec3d1.z, -d17, d17)));
++ if (baseblockposition.getY() != 0 && Mth.floor(this.getX()) - pos.getX() == baseblockposition.getX() && Mth.floor(this.getZ()) - pos.getZ() == baseblockposition.getZ()) {
++ this.setPos(this.getX(), this.getY() + (double) baseblockposition.getY(), this.getZ());
++ } else if (baseblockposition1.getY() != 0 && Mth.floor(this.getX()) - pos.getX() == baseblockposition1.getX() && Mth.floor(this.getZ()) - pos.getZ() == baseblockposition1.getZ()) {
++ this.setPos(this.getX(), this.getY() + (double) baseblockposition1.getY(), this.getZ());
+ }
+
+ this.applyNaturalSlowdown();
+- Vec3 pos2 = this.getPos(this.getX(), this.getY(), this.getZ());
+- if (pos2 != null && pos1 != null) {
+- double d13 = (pos1.y - pos2.y) * 0.05;
+- Vec3 deltaMovement2 = this.getDeltaMovement();
+- double d14 = deltaMovement2.horizontalDistance();
+- if (d14 > 0.0) {
+- this.setDeltaMovement(deltaMovement2.multiply((d14 + d13) / d14, 1.0, (d14 + d13) / d14));
++ Vec3 vec3d3 = this.getPos(this.getX(), this.getY(), this.getZ());
++ Vec3 vec3d4;
++ double d18;
++
++ if (vec3d3 != null && vec3d != null) {
++ double d19 = (vec3d.y - vec3d3.y) * 0.05D;
++
++ vec3d4 = this.getDeltaMovement();
++ d18 = vec3d4.horizontalDistance();
++ if (d18 > 0.0D) {
++ this.setDeltaMovement(vec3d4.multiply((d18 + d19) / d18, 1.0D, (d18 + d19) / d18));
+ }
+
+- this.setPos(this.getX(), pos2.y, this.getZ());
++ this.setPos(this.getX(), vec3d3.y, this.getZ());
+ }
+
+- int floor = Mth.floor(this.getX());
+- int floor1 = Mth.floor(this.getZ());
+- if (floor != pos.getX() || floor1 != pos.getZ()) {
+- Vec3 deltaMovement2 = this.getDeltaMovement();
+- double d14 = deltaMovement2.horizontalDistance();
+- this.setDeltaMovement(d14 * (double)(floor - pos.getX()), deltaMovement2.y, d14 * (double)(floor1 - pos.getZ()));
++ int i = Mth.floor(this.getX());
++ int j = Mth.floor(this.getZ());
++
++ if (i != pos.getX() || j != pos.getZ()) {
++ vec3d4 = this.getDeltaMovement();
++ d18 = vec3d4.horizontalDistance();
++ this.setDeltaMovement(d18 * (double) (i - pos.getX()), vec3d4.y, d18 * (double) (j - pos.getZ()));
+ }
+
+ if (flag) {
+- Vec3 deltaMovement2 = this.getDeltaMovement();
+- double d14 = deltaMovement2.horizontalDistance();
+- if (d14 > 0.01) {
+- double d15 = 0.06;
+- this.setDeltaMovement(deltaMovement2.add(deltaMovement2.x / d14 * 0.06, 0.0, deltaMovement2.z / d14 * 0.06));
++ vec3d4 = this.getDeltaMovement();
++ d18 = vec3d4.horizontalDistance();
++ if (d18 > 0.01D) {
++ double d20 = 0.06D;
++
++ this.setDeltaMovement(vec3d4.add(vec3d4.x / d18 * 0.06D, 0.0D, vec3d4.z / d18 * 0.06D));
+ } else {
+- Vec3 deltaMovement3 = this.getDeltaMovement();
+- double d16 = deltaMovement3.x;
+- double d17 = deltaMovement3.z;
+- if (railShape == RailShape.EAST_WEST) {
++ Vec3 vec3d5 = this.getDeltaMovement();
++ double d21 = vec3d5.x;
++ double d22 = vec3d5.z;
++
++ if (blockpropertytrackposition == RailShape.EAST_WEST) {
+ if (this.isRedstoneConductor(pos.west())) {
+- d16 = 0.02;
++ d21 = 0.02D;
+ } else if (this.isRedstoneConductor(pos.east())) {
+- d16 = -0.02;
++ d21 = -0.02D;
+ }
+ } else {
+- if (railShape != RailShape.NORTH_SOUTH) {
++ if (blockpropertytrackposition != RailShape.NORTH_SOUTH) {
+ return;
+ }
+
+ if (this.isRedstoneConductor(pos.north())) {
+- d17 = 0.02;
++ d22 = 0.02D;
+ } else if (this.isRedstoneConductor(pos.south())) {
+- d17 = -0.02;
++ d22 = -0.02D;
+ }
+ }
+
+- this.setDeltaMovement(d16, deltaMovement3.y, d17);
++ this.setDeltaMovement(d21, vec3d5.y, d22);
+ }
+ }
++
+ }
+
+ @Override
+@@ -514,100 +654,109 @@
+ }
+
+ protected void applyNaturalSlowdown() {
+- double d = this.isVehicle() ? 0.997 : 0.96;
+- Vec3 deltaMovement = this.getDeltaMovement();
+- Vec3 var4 = deltaMovement.multiply(d, 0.0, d);
++ double d0 = this.isVehicle() || !this.slowWhenEmpty ? 0.997D : 0.96D; // CraftBukkit - add !this.slowWhenEmpty
++ Vec3 vec3d = this.getDeltaMovement();
++
++ vec3d = vec3d.multiply(d0, 0.0D, d0);
+ if (this.isInWater()) {
+- var4 = var4.scale(0.95F);
++ vec3d = vec3d.scale(0.949999988079071D);
+ }
+
+- this.setDeltaMovement(var4);
++ this.setDeltaMovement(vec3d);
+ }
+
+ @Nullable
+- public Vec3 getPosOffs(double x, double y, double z, double offset) {
+- int floor = Mth.floor(x);
+- int floor1 = Mth.floor(y);
+- int floor2 = Mth.floor(z);
+- if (this.level().getBlockState(new BlockPos(floor, floor1 - 1, floor2)).is(BlockTags.RAILS)) {
+- floor1--;
++ public Vec3 getPosOffs(double x, double d1, double y, double d3) {
++ int i = Mth.floor(x);
++ int j = Mth.floor(d1);
++ int k = Mth.floor(y);
++
++ if (this.level().getBlockState(new BlockPos(i, j - 1, k)).is(BlockTags.RAILS)) {
++ --j;
+ }
+
+- BlockState blockState = this.level().getBlockState(new BlockPos(floor, floor1, floor2));
+- if (BaseRailBlock.isRail(blockState)) {
+- RailShape railShape = blockState.getValue(((BaseRailBlock)blockState.getBlock()).getShapeProperty());
+- double var24 = (double)floor1;
+- if (railShape.isAscending()) {
+- var24 = (double)(floor1 + 1);
++ IBlockData iblockdata = this.level().getBlockState(new BlockPos(i, j, k));
++
++ if (BaseRailBlock.isRail(iblockdata)) {
++ RailShape blockpropertytrackposition = (RailShape) iblockdata.getValue(((BaseRailBlock) iblockdata.getBlock()).getShapeProperty());
++
++ d1 = (double) j;
++ if (blockpropertytrackposition.isAscending()) {
++ d1 = (double) (j + 1);
+ }
+
+- Pair<Vec3i, Vec3i> pair = exits(railShape);
+- Vec3i vec3i = pair.getFirst();
+- Vec3i vec3i1 = pair.getSecond();
+- double d = (double)(vec3i1.getX() - vec3i.getX());
+- double d1 = (double)(vec3i1.getZ() - vec3i.getZ());
+- double squareRoot = Math.sqrt(d * d + d1 * d1);
+- d /= squareRoot;
+- d1 /= squareRoot;
+- double var23 = x + d * offset;
+- double var25 = z + d1 * offset;
+- if (vec3i.getY() != 0 && Mth.floor(var23) - floor == vec3i.getX() && Mth.floor(var25) - floor2 == vec3i.getZ()) {
+- var24 += (double)vec3i.getY();
+- } else if (vec3i1.getY() != 0 && Mth.floor(var23) - floor == vec3i1.getX() && Mth.floor(var25) - floor2 == vec3i1.getZ()) {
+- var24 += (double)vec3i1.getY();
++ Pair<Vec3i, Vec3i> pair = exits(blockpropertytrackposition);
++ Vec3i baseblockposition = (Vec3i) pair.getFirst();
++ Vec3i baseblockposition1 = (Vec3i) pair.getSecond();
++ double d4 = (double) (baseblockposition1.getX() - baseblockposition.getX());
++ double d5 = (double) (baseblockposition1.getZ() - baseblockposition.getZ());
++ double d6 = Math.sqrt(d4 * d4 + d5 * d5);
++
++ d4 /= d6;
++ d5 /= d6;
++ x += d4 * d3;
++ y += d5 * d3;
++ if (baseblockposition.getY() != 0 && Mth.floor(x) - i == baseblockposition.getX() && Mth.floor(y) - k == baseblockposition.getZ()) {
++ d1 += (double) baseblockposition.getY();
++ } else if (baseblockposition1.getY() != 0 && Mth.floor(x) - i == baseblockposition1.getX() && Mth.floor(y) - k == baseblockposition1.getZ()) {
++ d1 += (double) baseblockposition1.getY();
+ }
+
+- return this.getPos(var23, var24, var25);
++ return this.getPos(x, d1, y);
+ } else {
+ return null;
+ }
+ }
+
+ @Nullable
+- public Vec3 getPos(double x, double y, double z) {
+- int floor = Mth.floor(x);
+- int floor1 = Mth.floor(y);
+- int floor2 = Mth.floor(z);
+- if (this.level().getBlockState(new BlockPos(floor, floor1 - 1, floor2)).is(BlockTags.RAILS)) {
+- floor1--;
++ public Vec3 getPos(double x, double d1, double y) {
++ int i = Mth.floor(x);
++ int j = Mth.floor(d1);
++ int k = Mth.floor(y);
++
++ if (this.level().getBlockState(new BlockPos(i, j - 1, k)).is(BlockTags.RAILS)) {
++ --j;
+ }
+
+- BlockState blockState = this.level().getBlockState(new BlockPos(floor, floor1, floor2));
+- if (BaseRailBlock.isRail(blockState)) {
+- RailShape railShape = blockState.getValue(((BaseRailBlock)blockState.getBlock()).getShapeProperty());
+- Pair<Vec3i, Vec3i> pair = exits(railShape);
+- Vec3i vec3i = pair.getFirst();
+- Vec3i vec3i1 = pair.getSecond();
+- double d = (double)floor + 0.5 + (double)vec3i.getX() * 0.5;
+- double d1 = (double)floor1 + 0.0625 + (double)vec3i.getY() * 0.5;
+- double d2 = (double)floor2 + 0.5 + (double)vec3i.getZ() * 0.5;
+- double d3 = (double)floor + 0.5 + (double)vec3i1.getX() * 0.5;
+- double d4 = (double)floor1 + 0.0625 + (double)vec3i1.getY() * 0.5;
+- double d5 = (double)floor2 + 0.5 + (double)vec3i1.getZ() * 0.5;
+- double d6 = d3 - d;
+- double d7 = (d4 - d1) * 2.0;
+- double d8 = d5 - d2;
+- double d9;
+- if (d6 == 0.0) {
+- d9 = z - (double)floor2;
+- } else if (d8 == 0.0) {
+- d9 = x - (double)floor;
++ IBlockData iblockdata = this.level().getBlockState(new BlockPos(i, j, k));
++
++ if (BaseRailBlock.isRail(iblockdata)) {
++ RailShape blockpropertytrackposition = (RailShape) iblockdata.getValue(((BaseRailBlock) iblockdata.getBlock()).getShapeProperty());
++ Pair<Vec3i, Vec3i> pair = exits(blockpropertytrackposition);
++ Vec3i baseblockposition = (Vec3i) pair.getFirst();
++ Vec3i baseblockposition1 = (Vec3i) pair.getSecond();
++ double d3 = (double) i + 0.5D + (double) baseblockposition.getX() * 0.5D;
++ double d4 = (double) j + 0.0625D + (double) baseblockposition.getY() * 0.5D;
++ double d5 = (double) k + 0.5D + (double) baseblockposition.getZ() * 0.5D;
++ double d6 = (double) i + 0.5D + (double) baseblockposition1.getX() * 0.5D;
++ double d7 = (double) j + 0.0625D + (double) baseblockposition1.getY() * 0.5D;
++ double d8 = (double) k + 0.5D + (double) baseblockposition1.getZ() * 0.5D;
++ double d9 = d6 - d3;
++ double d10 = (d7 - d4) * 2.0D;
++ double d11 = d8 - d5;
++ double d12;
++
++ if (d9 == 0.0D) {
++ d12 = y - (double) k;
++ } else if (d11 == 0.0D) {
++ d12 = x - (double) i;
+ } else {
+- double d10 = x - d;
+- double d11 = z - d2;
+- d9 = (d10 * d6 + d11 * d8) * 2.0;
++ double d13 = x - d3;
++ double d14 = y - d5;
++
++ d12 = (d13 * d9 + d14 * d11) * 2.0D;
+ }
+
+- x = d + d6 * d9;
+- double var40 = d1 + d7 * d9;
+- z = d2 + d8 * d9;
+- if (d7 < 0.0) {
+- var40++;
+- } else if (d7 > 0.0) {
+- var40 += 0.5;
++ x = d3 + d9 * d12;
++ d1 = d4 + d10 * d12;
++ y = d5 + d11 * d12;
++ if (d10 < 0.0D) {
++ ++d1;
++ } else if (d10 > 0.0D) {
++ d1 += 0.5D;
+ }
+
+- return new Vec3(x, var40, z);
++ return new Vec3(x, d1, y);
+ } else {
+ return null;
+ }
+@@ -615,8 +764,9 @@
+
+ @Override
+ public AABB getBoundingBoxForCulling() {
+- AABB boundingBox = this.getBoundingBox();
+- return this.hasCustomDisplay() ? boundingBox.inflate((double)Math.abs(this.getDisplayOffset()) / 16.0) : boundingBox;
++ AABB axisalignedbb = this.getBoundingBox();
++
++ return this.hasCustomDisplay() ? axisalignedbb.inflate((double) Math.abs(this.getDisplayOffset()) / 16.0D) : axisalignedbb;
+ }
+
+ @Override
+@@ -625,6 +775,7 @@
+ this.setDisplayBlockState(NbtUtils.readBlockState(this.level().holderLookup(Registries.BLOCK), compound.getCompound("DisplayState")));
+ this.setDisplayOffset(compound.getInt("DisplayOffset"));
+ }
++
+ }
+
+ @Override
+@@ -634,6 +785,7 @@
+ compound.put("DisplayState", NbtUtils.writeBlockState(this.getDisplayBlockState()));
+ compound.putInt("DisplayOffset", this.getDisplayOffset());
+ }
++
+ }
+
+ @Override
+@@ -641,76 +793,83 @@
+ if (!this.level().isClientSide) {
+ if (!entity.noPhysics && !this.noPhysics) {
+ if (!this.hasPassenger(entity)) {
+- double d = entity.getX() - this.getX();
++ // CraftBukkit start
++ VehicleEntityCollisionEvent collisionEvent = new VehicleEntityCollisionEvent((Vehicle) this.getBukkitEntity(), entity.getBukkitEntity());
++ this.level().getCraftServer().getPluginManager().callEvent(collisionEvent);
++
++ if (collisionEvent.isCancelled()) {
++ return;
++ }
++ // CraftBukkit end
++ double d0 = entity.getX() - this.getX();
+ double d1 = entity.getZ() - this.getZ();
+- double d2 = d * d + d1 * d1;
+- if (d2 >= 1.0E-4F) {
++ double d2 = d0 * d0 + d1 * d1;
++
++ if (d2 >= 9.999999747378752E-5D) {
+ d2 = Math.sqrt(d2);
+- double var24 = d / d2;
+- double var28 = d1 / d2;
+- double d3 = 1.0 / d2;
+- if (d3 > 1.0) {
+- d3 = 1.0;
++ d0 /= d2;
++ d1 /= d2;
++ double d3 = 1.0D / d2;
++
++ if (d3 > 1.0D) {
++ d3 = 1.0D;
+ }
+
+- d = var24 * d3;
+- d1 = var28 * d3;
+- d *= 0.1F;
+- d1 *= 0.1F;
+- double var27 = d * 0.5;
+- double var31 = d1 * 0.5;
++ d0 *= d3;
++ d1 *= d3;
++ d0 *= 0.10000000149011612D;
++ d1 *= 0.10000000149011612D;
++ d0 *= 0.5D;
++ d1 *= 0.5D;
+ if (entity instanceof AbstractMinecart) {
+ double d4 = entity.getX() - this.getX();
+ double d5 = entity.getZ() - this.getZ();
+- Vec3 vec3 = new Vec3(d4, 0.0, d5).normalize();
+- Vec3 vec31 = new Vec3(
+- (double)Mth.cos(this.getYRot() * (float) (Math.PI / 180.0)),
+- 0.0,
+- (double)Mth.sin(this.getYRot() * (float) (Math.PI / 180.0))
+- )
+- .normalize();
+- double abs = Math.abs(vec3.dot(vec31));
+- if (abs < 0.8F) {
++ Vec3 vec3d = (new Vec3(d4, 0.0D, d5)).normalize();
++ Vec3 vec3d1 = (new Vec3((double) Mth.cos(this.getYRot() * 0.017453292F), 0.0D, (double) Mth.sin(this.getYRot() * 0.017453292F))).normalize();
++ double d6 = Math.abs(vec3d.dot(vec3d1));
++
++ if (d6 < 0.800000011920929D) {
+ return;
+ }
+
+- Vec3 deltaMovement = this.getDeltaMovement();
+- Vec3 deltaMovement1 = entity.getDeltaMovement();
+- if (((AbstractMinecart)entity).getMinecartType() == AbstractMinecart.Type.FURNACE
+- && this.getMinecartType() != AbstractMinecart.Type.FURNACE) {
+- this.setDeltaMovement(deltaMovement.multiply(0.2, 1.0, 0.2));
+- this.push(deltaMovement1.x - var27, 0.0, deltaMovement1.z - var31);
+- entity.setDeltaMovement(deltaMovement1.multiply(0.95, 1.0, 0.95));
+- } else if (((AbstractMinecart)entity).getMinecartType() != AbstractMinecart.Type.FURNACE
+- && this.getMinecartType() == AbstractMinecart.Type.FURNACE) {
+- entity.setDeltaMovement(deltaMovement1.multiply(0.2, 1.0, 0.2));
+- entity.push(deltaMovement.x + var27, 0.0, deltaMovement.z + var31);
+- this.setDeltaMovement(deltaMovement.multiply(0.95, 1.0, 0.95));
++ Vec3 vec3d2 = this.getDeltaMovement();
++ Vec3 vec3d3 = entity.getDeltaMovement();
++
++ if (((AbstractMinecart) entity).getMinecartType() == AbstractMinecart.EnumMinecartType.FURNACE && this.getMinecartType() != AbstractMinecart.EnumMinecartType.FURNACE) {
++ this.setDeltaMovement(vec3d2.multiply(0.2D, 1.0D, 0.2D));
++ this.push(vec3d3.x - d0, 0.0D, vec3d3.z - d1);
++ entity.setDeltaMovement(vec3d3.multiply(0.95D, 1.0D, 0.95D));
++ } else if (((AbstractMinecart) entity).getMinecartType() != AbstractMinecart.EnumMinecartType.FURNACE && this.getMinecartType() == AbstractMinecart.EnumMinecartType.FURNACE) {
++ entity.setDeltaMovement(vec3d3.multiply(0.2D, 1.0D, 0.2D));
++ entity.push(vec3d2.x + d0, 0.0D, vec3d2.z + d1);
++ this.setDeltaMovement(vec3d2.multiply(0.95D, 1.0D, 0.95D));
+ } else {
+- double d6 = (deltaMovement1.x + deltaMovement.x) / 2.0;
+- double d7 = (deltaMovement1.z + deltaMovement.z) / 2.0;
+- this.setDeltaMovement(deltaMovement.multiply(0.2, 1.0, 0.2));
+- this.push(d6 - var27, 0.0, d7 - var31);
+- entity.setDeltaMovement(deltaMovement1.multiply(0.2, 1.0, 0.2));
+- entity.push(d6 + var27, 0.0, d7 + var31);
++ double d7 = (vec3d3.x + vec3d2.x) / 2.0D;
++ double d8 = (vec3d3.z + vec3d2.z) / 2.0D;
++
++ this.setDeltaMovement(vec3d2.multiply(0.2D, 1.0D, 0.2D));
++ this.push(d7 - d0, 0.0D, d8 - d1);
++ entity.setDeltaMovement(vec3d3.multiply(0.2D, 1.0D, 0.2D));
++ entity.push(d7 + d0, 0.0D, d8 + d1);
+ }
+ } else {
+- this.push(-var27, 0.0, -var31);
+- entity.push(var27 / 4.0, 0.0, var31 / 4.0);
++ this.push(-d0, 0.0D, -d1);
++ entity.push(d0 / 4.0D, 0.0D, d1 / 4.0D);
+ }
+ }
++
+ }
+ }
+ }
+ }
+
+ @Override
+- public void lerpTo(double d, double d1, double d2, float f, float f1, int i) {
+- this.lerpX = d;
++ public void lerpTo(double d0, double d1, double d2, float f, float f1, int i) {
++ this.lerpX = d0;
+ this.lerpY = d1;
+ this.lerpZ = d2;
+- this.lerpYRot = (double)f;
+- this.lerpXRot = (double)f1;
++ this.lerpYRot = (double) f;
++ this.lerpXRot = (double) f1;
+ this.lerpSteps = i + 2;
+ this.setDeltaMovement(this.targetDeltaMovement);
+ }
+@@ -732,75 +891,110 @@
+
+ @Override
+ public float lerpTargetXRot() {
+- return this.lerpSteps > 0 ? (float)this.lerpXRot : this.getXRot();
++ return this.lerpSteps > 0 ? (float) this.lerpXRot : this.getXRot();
+ }
+
+ @Override
+ public float lerpTargetYRot() {
+- return this.lerpSteps > 0 ? (float)this.lerpYRot : this.getYRot();
++ return this.lerpSteps > 0 ? (float) this.lerpYRot : this.getYRot();
+ }
+
+ @Override
+- public void lerpMotion(double x, double y, double z) {
+- this.targetDeltaMovement = new Vec3(x, y, z);
++ public void lerpMotion(double x, double d1, double y) {
++ this.targetDeltaMovement = new Vec3(x, d1, y);
+ this.setDeltaMovement(this.targetDeltaMovement);
+ }
+
+- public abstract AbstractMinecart.Type getMinecartType();
++ public abstract AbstractMinecart.EnumMinecartType getMinecartType();
+
+- public BlockState getDisplayBlockState() {
+- return !this.hasCustomDisplay() ? this.getDefaultDisplayBlockState() : Block.stateById(this.getEntityData().get(DATA_ID_DISPLAY_BLOCK));
++ public IBlockData getDisplayBlockState() {
++ return !this.hasCustomDisplay() ? this.getDefaultDisplayBlockState() : Block.stateById((Integer) this.getEntityData().get(AbstractMinecart.DATA_ID_DISPLAY_BLOCK));
+ }
+
+- public BlockState getDefaultDisplayBlockState() {
++ public IBlockData getDefaultDisplayBlockState() {
+ return Blocks.AIR.defaultBlockState();
+ }
+
+ public int getDisplayOffset() {
+- return !this.hasCustomDisplay() ? this.getDefaultDisplayOffset() : this.getEntityData().get(DATA_ID_DISPLAY_OFFSET);
++ return !this.hasCustomDisplay() ? this.getDefaultDisplayOffset() : (Integer) this.getEntityData().get(AbstractMinecart.DATA_ID_DISPLAY_OFFSET);
+ }
+
+ public int getDefaultDisplayOffset() {
+ return 6;
+ }
+
+- public void setDisplayBlockState(BlockState displayState) {
+- this.getEntityData().set(DATA_ID_DISPLAY_BLOCK, Block.getId(displayState));
++ public void setDisplayBlockState(IBlockData displayState) {
++ this.getEntityData().set(AbstractMinecart.DATA_ID_DISPLAY_BLOCK, Block.getId(displayState));
+ this.setCustomDisplay(true);
+ }
+
+ public void setDisplayOffset(int displayOffset) {
+- this.getEntityData().set(DATA_ID_DISPLAY_OFFSET, displayOffset);
++ this.getEntityData().set(AbstractMinecart.DATA_ID_DISPLAY_OFFSET, displayOffset);
+ this.setCustomDisplay(true);
+ }
+
+ public boolean hasCustomDisplay() {
+- return this.getEntityData().get(DATA_ID_CUSTOM_DISPLAY);
++ return (Boolean) this.getEntityData().get(AbstractMinecart.DATA_ID_CUSTOM_DISPLAY);
+ }
+
+ public void setCustomDisplay(boolean customDisplay) {
+- this.getEntityData().set(DATA_ID_CUSTOM_DISPLAY, customDisplay);
++ this.getEntityData().set(AbstractMinecart.DATA_ID_CUSTOM_DISPLAY, customDisplay);
+ }
+
+ @Override
+ public ItemStack getPickResult() {
+- return new ItemStack(switch (this.getMinecartType()) {
+- case CHEST -> Items.CHEST_MINECART;
+- case FURNACE -> Items.FURNACE_MINECART;
+- case TNT -> Items.TNT_MINECART;
+- default -> Items.MINECART;
+- case HOPPER -> Items.HOPPER_MINECART;
+- case COMMAND_BLOCK -> Items.COMMAND_BLOCK_MINECART;
+- });
++ Item item;
++
++ switch (this.getMinecartType()) {
++ case CHEST:
++ item = Items.CHEST_MINECART;
++ break;
++ case FURNACE:
++ item = Items.FURNACE_MINECART;
++ break;
++ case TNT:
++ item = Items.TNT_MINECART;
++ break;
++ case SPAWNER:
++ default:
++ item = Items.MINECART;
++ break;
++ case HOPPER:
++ item = Items.HOPPER_MINECART;
++ break;
++ case COMMAND_BLOCK:
++ item = Items.COMMAND_BLOCK_MINECART;
++ }
++
++ return new ItemStack(item);
+ }
+
+- public static enum Type {
+- RIDEABLE,
+- CHEST,
+- FURNACE,
+- TNT,
+- SPAWNER,
+- HOPPER,
+- COMMAND_BLOCK;
++ public static enum EnumMinecartType {
++
++ RIDEABLE, CHEST, FURNACE, TNT, SPAWNER, HOPPER, COMMAND_BLOCK;
++
++ private EnumMinecartType() {}
+ }
++
++ // CraftBukkit start - Methods for getting and setting flying and derailed velocity modifiers
++ public Vector getFlyingVelocityMod() {
++ return new Vector(flyingX, flyingY, flyingZ);
++ }
++
++ public void setFlyingVelocityMod(Vector flying) {
++ flyingX = flying.getX();
++ flyingY = flying.getY();
++ flyingZ = flying.getZ();
++ }
++
++ public Vector getDerailedVelocityMod() {
++ return new Vector(derailedX, derailedY, derailedZ);
++ }
++
++ public void setDerailedVelocityMod(Vector derailed) {
++ derailedX = derailed.getX();
++ derailedY = derailed.getY();
++ derailedZ = derailed.getZ();
++ }
++ // CraftBukkit end
+ }