gt:                 double d1 = this.locY + (this.f - this.locY) / (double) this.d;
context: package net.minecraft.server;
  
  import java.util.List;
  
  // CraftBukkit start
  import org.bukkit.Location;
  import org.bukkit.entity.Vehicle;
  import org.bukkit.event.vehicle.VehicleDamageEvent;
  import org.bukkit.event.vehicle.VehicleDestroyEvent;
  import org.bukkit.event.vehicle.VehicleEntityCollisionEvent;
  import org.bukkit.util.Vector;
  // CraftBukkit end
  
  public abstract class EntityMinecartAbstract extends Entity {
  
      private boolean a;
      private String b;
      private static final int[][][] matrix = new int[][][] { { { 0, 0, -1}, { 0, 0, 1}}, { { -1, 0, 0}, { 1, 0, 0}}, { { -1, -1, 0}, { 1, 0, 0}}, { { -1, 0, 0}, { 1, -1, 0}}, { { 0, 0, -1}, { 0, -1, 1}}, { { 0, -1, -1}, { 0, 0, 1}}, { { 0, 0, 1}, { 1, 0, 0}}, { { 0, 0, 1}, { -1, 0, 0}}, { { 0, 0, -1}, { -1, 0, 0}}, { { 0, 0, -1}, { 1, 0, 0}}};
      private int d;
      private double e;
      private double f;
      private double g;
      private double h;
      private double i;
  
      // 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
  
      public EntityMinecartAbstract(World world) {
          super(world);
          this.k = true;
          this.a(0.98F, 0.7F);
          this.height = this.length / 2.0F;
      }
  
      public static EntityMinecartAbstract a(World world, double d0, double d1, double d2, int i) {
          switch (i) {
          case 1:
              return new EntityMinecartChest(world, d0, d1, d2);
  
          case 2:
              return new EntityMinecartFurnace(world, d0, d1, d2);
  
          case 3:
              return new EntityMinecartTNT(world, d0, d1, d2);
  
          case 4:
              return new EntityMinecartMobSpawner(world, d0, d1, d2);
  
          case 5:
              return new EntityMinecartHopper(world, d0, d1, d2);
  
          case 6:
              return new EntityMinecartCommandBlock(world, d0, d1, d2);
  
          default:
              return new EntityMinecartRideable(world, d0, d1, d2);
          }
      }
  
      protected boolean g_() {
          return false;
      }
  
      protected void c() {
          this.datawatcher.a(17, new Integer(0));
          this.datawatcher.a(18, new Integer(1));
          this.datawatcher.a(19, new Float(0.0F));
          this.datawatcher.a(20, new org.spigotmc.ProtocolData.DualInt(0, 0)); // Spigot - protocol patch
          this.datawatcher.a(21, new Integer(6));
          this.datawatcher.a(22, Byte.valueOf((byte) 0));
      }
  
      public AxisAlignedBB h(Entity entity) {
          return entity.S() ? entity.boundingBox : null;
      }
  
      public AxisAlignedBB J() {
          return null;
      }
  
      public boolean S() {
          return true;
      }
  
      public EntityMinecartAbstract(World world, double d0, double d1, double d2) {
          this(world);
          this.setPosition(d0, d1, d2);
          this.motX = 0.0D;
          this.motY = 0.0D;
          this.motZ = 0.0D;
          this.lastX = d0;
          this.lastY = d1;
          this.lastZ = d2;
  
          this.world.getServer().getPluginManager().callEvent(new org.bukkit.event.vehicle.VehicleCreateEvent((Vehicle) this.getBukkitEntity())); // CraftBukkit
      }
  
      public double ae() {
          return (double) this.length * 0.0D - 0.30000001192092896D;
      }
  
      public boolean damageEntity(DamageSource damagesource, float f) {
          if (!this.world.isStatic && !this.dead) {
              if (this.isInvulnerable()) {
                  return false;
              } else {
                  // CraftBukkit start - fire VehicleDamageEvent
                  Vehicle vehicle = (Vehicle) this.getBukkitEntity();
                  org.bukkit.entity.Entity passenger = (damagesource.getEntity() == null) ? null : damagesource.getEntity().getBukkitEntity();
  
                  VehicleDamageEvent event = new VehicleDamageEvent(vehicle, passenger, f);
                  this.world.getServer().getPluginManager().callEvent(event);
  
                  if (event.isCancelled()) {
                      return true;
                  }
  
                  f = (float) event.getDamage();
                  // CraftBukkit end
  
                  this.j(-this.l());
                  this.c(10);
                  this.Q();
                  this.setDamage(this.getDamage() + f * 10.0F);
                  boolean flag = damagesource.getEntity() instanceof EntityHuman && ((EntityHuman) damagesource.getEntity()).abilities.canInstantlyBuild;
  
                  if (flag || this.getDamage() > 40.0F) {
                      if (this.passenger != null) {
                          this.passenger.mount(this);
                      }
  
                      // CraftBukkit start
                      VehicleDestroyEvent destroyEvent = new VehicleDestroyEvent(vehicle, passenger);
                      this.world.getServer().getPluginManager().callEvent(destroyEvent);
  
                      if (destroyEvent.isCancelled()) {
                          this.setDamage(40); // Maximize damage so this doesn't get triggered again right away
                          return true;
                      }
                      // CraftBukkit end
  
                      if (flag && !this.k_()) {
                          this.die();
                      } else {
                          this.a(damagesource);
                      }
                  }
  
                  return true;
              }
          } else {
              return true;
          }
      }
  
      public void a(DamageSource damagesource) {
          this.die();
          ItemStack itemstack = new ItemStack(Items.MINECART, 1);
  
          if (this.b != null) {
              itemstack.c(this.b);
          }
  
          this.a(itemstack, 0.0F);
      }
  
      public boolean R() {
          return !this.dead;
      }
  
      public void die() {
          super.die();
      }
  
      public void h() {
          // CraftBukkit start
          double prevX = this.locX;
          double prevY = this.locY;
          double prevZ = this.locZ;
          float prevYaw = this.yaw;
          float prevPitch = this.pitch;
          // CraftBukkit end
  
          if (this.getType() > 0) {
              this.c(this.getType() - 1);
          }
  
          if (this.getDamage() > 0.0F) {
              this.setDamage(this.getDamage() - 1.0F);
          }
  
          if (this.locY < -64.0D) {
              this.G();
          }
  
          int i;
  
          if (!this.world.isStatic && this.world instanceof WorldServer) {
              this.world.methodProfiler.a("portal");
              MinecraftServer minecraftserver = ((WorldServer) this.world).getMinecraftServer();
  
              i = this.D();
              if (this.an) {
                  if (true || minecraftserver.getAllowNether()) { // CraftBukkit - multi-world should still allow teleport even if default vanilla nether disabled
                      if (this.vehicle == null && this.ao++ >= i) {
                          this.ao = i;
                          this.portalCooldown = this.ai();
                          byte b0;
  
                          if (this.world.worldProvider.dimension == -1) {
                              b0 = 0;
                          } else {
                              b0 = -1;
                          }
  
                          this.b(b0);
                      }
  
                      this.an = false;
                  }
              } else {
                  if (this.ao > 0) {
                      this.ao -= 4;
                  }
  
                  if (this.ao < 0) {
                      this.ao = 0;
                  }
              }
  
              if (this.portalCooldown > 0) {
                  --this.portalCooldown;
              }
  
              this.world.methodProfiler.b();
          }
  
          if (this.world.isStatic) {
              if (this.d > 0) {
                  double d0 = this.locX + (this.e - this.locX) / (double) this.d;
  