<commit_before>--- ../src_base/minecraft/net/minecraft/entity/item/EntityMinecart.java
+++ ../src_work/minecraft/net/minecraft/entity/item/EntityMinecart.java
@@ -2,6 +2,8 @@
 
 import cpw.mods.fml.relauncher.Side;
 import cpw.mods.fml.relauncher.SideOnly;
+
+import java.util.ArrayList;
 import java.util.List;
 import net.minecraft.block.Block;
 import net.minecraft.block.BlockRail;
@@ -23,6 +25,11 @@
 import net.minecraft.world.World;
 import net.minecraft.world.WorldServer;
 
+import net.minecraftforge.common.IMinecartCollisionHandler;
+import net.minecraftforge.common.MinecartRegistry;
+import net.minecraftforge.common.MinecraftForge;
+import net.minecraftforge.event.entity.minecart.*;
+
 public class EntityMinecart extends Entity implements IInventory
 {
     /** Array of item stacks stored in minecart (for storage minecarts). */
@@ -52,6 +59,25 @@
     @SideOnly(Side.CLIENT)
     protected double velocityZ;
 
+    /* Forge: Minecart Compatibility Layer Integration. */
+    public static float defaultMaxSpeedRail = 0.4f;
+    public static float defaultMaxSpeedGround = 0.4f;
+    public static float defaultMaxSpeedAirLateral = 0.4f;
+    public static float defaultMaxSpeedAirVertical = -1f;
+    public static double defaultDragRidden = 0.996999979019165D;
+    public static double defaultDragEmpty = 0.9599999785423279D;
+    public static double defaultDragAir = 0.94999998807907104D;
+    protected boolean canUseRail = true;
+    protected boolean canBePushed = true;
+    private static IMinecartCollisionHandler collisionHandler = null;
+
+    /* Instance versions of the above physics properties */
+    protected float maxSpeedRail;
+    protected float maxSpeedGround;
+    protected float maxSpeedAirLateral;
+    protected float maxSpeedAirVertical;
+    protected double dragAir;
+
     public EntityMinecart(World par1World)
     {
         super(par1World);
@@ -63,6 +89,18 @@
         this.setSize(0.98F, 0.7F);
         this.yOffset = this.height / 2.0F;
         this.field_82344_g = par1World != null ? par1World.func_82735_a(this) : null;
+
+        maxSpeedRail = defaultMaxSpeedRail;
+        maxSpeedGround = defaultMaxSpeedGround;
+        maxSpeedAirLateral = defaultMaxSpeedAirLateral;
+        maxSpeedAirVertical = defaultMaxSpeedAirVertical;
+        dragAir = defaultDragAir;
+    }
+
+    public EntityMinecart(World world, int type)
+    {
+        this(world);
+        minecartType = type;
     }
 
     /**
@@ -88,6 +126,10 @@
      */
     public AxisAlignedBB getCollisionBox(Entity par1Entity)
     {
+        if (getCollisionHandler() != null)
+        {
+            return getCollisionHandler().getCollisionBox(this, par1Entity);
+        }
         return par1Entity.canBePushed() ? par1Entity.boundingBox : null;
     }
 
@@ -96,6 +138,10 @@
      */
     public AxisAlignedBB getBoundingBox()
     {
+        if (getCollisionHandler() != null)
+        {
+            return getCollisionHandler().getBoundingBox(this);
+        }
         return null;
     }
 
@@ -104,7 +150,7 @@
      */
     public boolean canBePushed()
     {
-        return true;
+        return canBePushed;
     }
 
     public EntityMinecart(World par1World, double par2, double par4, double par6, int par8)
@@ -159,48 +205,7 @@
                     }
 
                     this.setDead();
-                    this.dropItemWithOffset(Item.minecartEmpty.shiftedIndex, 1, 0.0F);
-
-                    if (this.minecartType == 1)
-                    {
-                        EntityMinecart var3 = this;
-
-                        for (int var4 = 0; var4 < var3.getSizeInventory(); ++var4)
-                        {
-                            ItemStack var5 = var3.getStackInSlot(var4);
-
-                            if (var5 != null)
-                            {
-                                float var6 = this.rand.nextFloat() * 0.8F + 0.1F;
-                                float var7 = this.rand.nextFloat() * 0.8F + 0.1F;
-                                float var8 = this.rand.nextFloat() * 0.8F + 0.1F;
-
-                                while (var5.stackSize > 0)
-                                {
-                                    int var9 = this.rand.nextInt(21) + 10;
-
-                                    if (var9 > var5.stackSize)
-                                    {
-                                        var9 = var5.stackSize;
-                                    }
-
-                                    var5.stackSize -= var9;
-                                    EntityItem var10 = new EntityItem(this.worldObj, this.posX + (double)var6, this.posY + (double)var7, this.posZ + (double)var8, new ItemStack(var5.itemID, var9, var5.getItemDamage()));
-                                    float var11 = 0.05F;
-                                    var10.motionX = (double)((float)this.rand.nextGaussian() * var11);
-                                    var10.motionY = (double)((float)this.rand.nextGaussian() * var11 + 0.2F);
-                                    var10.motionZ = (double)((float)this.rand.nextGaussian() * var11);
-                                    this.worldObj.spawnEntityInWorld(var10);
-                                }
-                            }
-                        }
-
-                        this.dropItemWithOffset(Block.chest.blockID, 1, 0.0F);
-                    }
-                    else if (this.minecartType == 2)
-                    {
-                        this.dropItemWithOffset(Block.stoneOvenIdle.blockID, 1, 0.0F);
-                    }
+                    dropCartAsItem();
                 }
 
                 return true;
@@ -318,7 +323,7 @@
             this.kill();
         }
 
-        if (this.isMinecartPowered() && this.rand.nextInt(4) == 0)
+        if (this.isMinecartPowered() && this.rand.nextInt(4) == 0 && minecartType == 2 && getClass() == EntityMinecart.class)
         {
             this.worldObj.spawnParticle("largesmoke", this.posX, this.posY + 0.8D, this.posZ, 0.0D, 0.0D, 0.0D);
         }
@@ -441,25 +446,7 @@
                     this.posY = (double)(var2 + 1);
                 }
 
-                if (var10 == 2)
-                {
-                    this.motionX -= var6;
-                }
-
-                if (var10 == 3)
-                {
-                    this.motionX += var6;
-                }
-
-                if (var10 == 4)
-                {
-                    this.motionZ += var6;
-                }
-
-                if (var10 == 5)
-                {
-                    this.motionZ -= var6;
-                }
+                adjustSlopeVelocities(var10);
 
                 int[][] var13 = field_70500_g[var10];
                 double var14 = (double)(var13[1][0] - var13[0][0]);
@@ -492,7 +479,7 @@
                     }
                 }
 
-                if (var12)
+                if (var12 && shouldDoRailFunctions())
                 {
                     var24 = Math.sqrt(this.motionX * this.motionX + this.motionZ * this.motionZ);
 
@@ -540,36 +527,8 @@
                 this.posX = var26 + var14 * var24;
                 this.posZ = var28 + var16 * var24;
                 this.setPosition(this.posX, this.posY + (double)this.yOffset, this.posZ);
-                var34 = this.motionX;
-                var36 = this.motionZ;
-
-                if (this.riddenByEntity != null)
-                {
-                    var34 *= 0.75D;
-                    var36 *= 0.75D;
-                }
-
-                if (var34 < -var4)
-                {
-                    var34 = -var4;
-                }
-
-                if (var34 > var4)
-                {
-                    var34 = var4;
-                }
-
-                if (var36 < -var4)
-                {
-                    var36 = -var4;
-                }
-
-                if (var36 > var4)
-                {
-                    var36 = var4;
-                }
-
-                this.moveEntity(var34, 0.0D, var36);
+
+                moveMinecartOnRail(var45, var2, var47);
 
                 if (var13[0][1] != 0 && MathHelper.floor_double(this.posX) - var45 == var13[0][0] && MathHelper.floor_double(this.posZ) - var47 == var13[0][2])
                 {
@@ -580,42 +539,7 @@
                     this.setPosition(this.posX, this.posY + (double)var13[1][1], this.posZ);
                 }
 
-                if (this.riddenByEntity != null)
-                {
-                    this.motionX *= 0.996999979019165D;
-                    this.motionY *= 0.0D;
-                    this.motionZ *= 0.996999979019165D;
-                }
-                else
-                {
-                    if (this.minecartType == 2)
-                    {
-                        double var38 = this.pushX * this.pushX + this.pushZ * this.pushZ;
-
-                        if (var38 > 1.0E-4D)
-                        {
-                            var38 = (double)MathHelper.sqrt_double(var38);
-                            this.pushX /= var38;
-                            this.pushZ /= var38;
-                            double var40 = 0.04D;
-                            this.motionX *= 0.800000011920929D;
-                            this.motionY *= 0.0D;
-                            this.motionZ *= 0.800000011920929D;
-                            this.motionX += this.pushX * var40;
-                            this.motionZ += this.pushZ * var40;
-                        }
-                        else
-                        {
-                            this.motionX *= 0.8999999761581421D;
-                            this.motionY *= 0.0D;
-                            this.motionZ *= 0.8999999761581421D;
-                        }
-                    }
-
-                    this.motionX *= 0.9599999785423279D;
-                    this.motionY *= 0.0D;
-                    this.motionZ *= 0.9599999785423279D;
-                }
+                applyDragAndPushForces();
 
                 Vec3 var54 = this.func_70489_a(this.posX, this.posY, this.posZ);
 
@@ -645,30 +569,14 @@
 
                 double var41;
 
-                if (this.minecartType == 2)
-                {
-                    var41 = this.pushX * this.pushX + this.pushZ * this.pushZ;
-
-                    if (var41 > 1.0E-4D && this.motionX * this.motionX + this.motionZ * this.motionZ > 0.001D)
-                    {
-                        var41 = (double)MathHelper.sqrt_double(var41);
-                        this.pushX /= var41;
-                        this.pushZ /= var41;
-
-                        if (this.pushX * this.motionX + this.pushZ * this.motionZ < 0.0D)
-                        {
-                            this.pushX = 0.0D;
-                            this.pushZ = 0.0D;
-                        }
-                        else
-                        {
-                            this.pushX = this.motionX;
-                            this.pushZ = this.motionZ;
-                        }
-                    }
-                }
-
-                if (var11)
+                updatePushForces();
+
+                if(shouldDoRailFunctions())
+                {
+                    ((BlockRail)Block.blocksList[var8]).onMinecartPass(worldObj, this, var45, var2, var47);
+                }
+
+                if (var11 && shouldDoRailFunctions())
                 {
                     var41 = Math.sqrt(this.motionX * this.motionX + this.motionZ * this.motionZ);
 
@@ -704,41 +612,7 @@
             }
             else
             {
-                if (this.motionX < -var4)
-                {
-                    this.motionX = -var4;
-                }
-
-                if (this.motionX > var4)
-                {
-                    this.motionX = var4;
-                }
-
-                if (this.motionZ < -var4)
-                {
-                    this.motionZ = -var4;
-                }
-
-                if (this.motionZ > var4)
-                {
-                    this.motionZ = var4;
-                }
-
-                if (this.onGround)
-                {
-                    this.motionX *= 0.5D;
-                    this.motionY *= 0.5D;
-                    this.motionZ *= 0.5D;
-                }
-
-                this.moveEntity(this.motionX, this.motionY, this.motionZ);
-
-                if (!this.onGround)
-                {
-                    this.motionX *= 0.949999988079071D;
-                    this.motionY *= 0.949999988079071D;
-                    this.motionZ *= 0.949999988079071D;
-                }
+                moveMinecartOffRail(var45, var2, var47);
             }
 
             this.doBlockCollisions();
@@ -765,7 +639,18 @@
             }
 
             this.setRotation(this.rotationYaw, this.rotationPitch);
-            List var15 = this.worldObj.getEntitiesWithinAABBExcludingEntity(this, this.boundingBox.expand(0.20000000298023224D, 0.0D, 0.20000000298023224D));
+
+            AxisAlignedBB box = null;
+            if (getCollisionHandler() != null)
+            {
+                box = getCollisionHandler().getMinecartCollisionBox(this);
+            }
+            else
+            {
+                box = boundingBox.expand(0.2D, 0.0D, 0.2D);
+            }
+
+            List var15 = this.worldObj.getEntitiesWithinAABBExcludingEntity(this, box);
 
             if (var15 != null && !var15.isEmpty())
             {
@@ -790,17 +675,8 @@
                 this.riddenByEntity = null;
             }
 
-            if (this.fuel > 0)
-            {
-                --this.fuel;
-            }
-
-            if (this.fuel <= 0)
-            {
-                this.pushX = this.pushZ = 0.0D;
-            }
-
-            this.setMinecartPowered(this.fuel > 0);
+            updateFuel();
+            MinecraftForge.EVENT_BUS.post(new MinecartUpdateEvent(this, var45, var2, var47));
         }
     }
 
@@ -824,12 +700,7 @@
         }
         else
         {
-            int var13 = this.worldObj.getBlockMetadata(var9, var10, var11);
-
-            if (((BlockRail)Block.blocksList[var12]).isPowered())
-            {
-                var13 &= 7;
-            }
+            int var13 = ((BlockRail)Block.blocksList[var12]).getBasicRailMetadata(worldObj, this, var9, var10, var11);
 
             par3 = (double)var10;
 
@@ -875,13 +746,8 @@
 
         if (BlockRail.isRailBlock(var10))
         {
-            int var11 = this.worldObj.getBlockMetadata(var7, var8, var9);
+            int var11 = ((BlockRail)Block.blocksList[var10]).getBasicRailMetadata(worldObj, this, var7, var8, var9);
             par3 = (double)var8;
-
-            if (((BlockRail)Block.blocksList[var10]).isPowered())
-            {
-                var11 &= 7;
-            }
 
             if (var11 >= 2 && var11 <= 5)
             {
@@ -946,13 +812,14 @@
     {
         par1NBTTagCompound.setInteger("Type", this.minecartType);
 
-        if (this.minecartType == 2)
+        if (isPoweredCart())
         {
             par1NBTTagCompound.setDouble("PushX", this.pushX);
             par1NBTTagCompound.setDouble("PushZ", this.pushZ);
-            par1NBTTagCompound.setShort("Fuel", (short)this.fuel);
-        }
-        else if (this.minecartType == 1)
+            par1NBTTagCompound.setInteger("Fuel", this.fuel);
+        }
+
+        if (getSizeInventory() > 0)
         {
             NBTTagList var2 = new NBTTagList();
 
@@ -978,13 +845,21 @@
     {
         this.minecartType = par1NBTTagCompound.getInteger("Type");
 
-        if (this.minecartType == 2)
+        if (isPoweredCart())
         {
             this.pushX = par1NBTTagCompound.getDouble("PushX");
             this.pushZ = par1NBTTagCompound.getDouble("PushZ");
-            this.fuel = par1NBTTagCompound.getShort("Fuel");
-        }
-        else if (this.minecartType == 1)
+            try
+            {
+                this.fuel = par1NBTTagCompound.getInteger("Fuel");
+            }
+            catch (ClassCastException e)
+            {
+                this.fuel = par1NBTTagCompound.getShort("Fuel");
+            }
+        }
+
+        if (getSizeInventory() > 0)
         {
             NBTTagList var2 = par1NBTTagCompound.getTagList("Items");
             this.cargoItems = new ItemStack[this.getSizeInventory()];
@@ -1013,11 +888,17 @@
      */
     public void applyEntityCollision(Entity par1Entity)
     {
+        MinecraftForge.EVENT_BUS.post(new MinecartCollisionEvent(this, par1Entity));
+        if (getCollisionHandler() != null)
+        {
+            getCollisionHandler().onEntityCollision(this, par1Entity);
+            return;
+        }
         if (!this.worldObj.isRemote)
         {
             if (par1Entity != this.riddenByEntity)
             {
-                if (par1Entity instanceof EntityLiving && !(par1Entity instanceof EntityPlayer) && !(par1Entity instanceof EntityIronGolem) && this.minecartType == 0 && this.motionX * this.motionX + this.motionZ * this.motionZ > 0.01D && this.riddenByEntity == null && par1Entity.ridingEntity == null)
+                if (par1Entity instanceof EntityLiving && !(par1Entity instanceof EntityPlayer) && !(par1Entity instanceof EntityIronGolem) && canBeRidden() && this.motionX * this.motionX + this.motionZ * this.motionZ > 0.01D && this.riddenByEntity == null && par1Entity.ridingEntity == null)
                 {
                     par1Entity.mountEntity(this);
                 }
@@ -1063,7 +944,7 @@
                         double var18 = par1Entity.motionX + this.motionX;
                         double var20 = par1Entity.motionZ + this.motionZ;
 
-                        if (((EntityMinecart)par1Entity).minecartType == 2 && this.minecartType != 2)
+                        if (((EntityMinecart)par1Entity).isPoweredCart() && !isPoweredCart())
                         {
                             this.motionX *= 0.20000000298023224D;
                             this.motionZ *= 0.20000000298023224D;
@@ -1071,7 +952,7 @@
                             par1Entity.motionX *= 0.949999988079071D;
                             par1Entity.motionZ *= 0.949999988079071D;
                         }
-                        else if (((EntityMinecart)par1Entity).minecartType != 2 && this.minecartType == 2)
+                        else if (!((EntityMinecart)par1Entity).isPoweredCart() && isPoweredCart())
                         {
                             par1Entity.motionX *= 0.20000000298023224D;
                             par1Entity.motionZ *= 0.20000000298023224D;
@@ -1106,7 +987,7 @@
      */
     public int getSizeInventory()
     {
-        return 27;
+        return (minecartType == 1 && getClass() == EntityMinecart.class ? 27 : 0);
     }
 
     /**
@@ -1209,7 +1090,12 @@
      */
     public boolean interact(EntityPlayer par1EntityPlayer)
     {
-        if (this.minecartType == 0)
+        if (MinecraftForge.EVENT_BUS.post(new MinecartInteractEvent(this, par1EntityPlayer)))
+        {
+            return true;
+        }
+
+        if (canBeRidden())
         {
             if (this.riddenByEntity != null && this.riddenByEntity instanceof EntityPlayer && this.riddenByEntity != par1EntityPlayer)
             {
@@ -1221,14 +1107,14 @@
                 par1EntityPlayer.mountEntity(this);
             }
         }
-        else if (this.minecartType == 1)
+        else if (getSizeInventory() > 0)
         {
             if (!this.worldObj.isRemote)
             {
                 par1EntityPlayer.displayGUIChest(this);
             }
         }
-        else if (this.minecartType == 2)
+        else if (this.minecartType == 2 && getClass() == EntityMinecart.class)
         {
             ItemStack var2 = par1EntityPlayer.inventory.getCurrentItem();
 
@@ -1352,4 +1238,375 @@
     {
         return this.dataWatcher.getWatchableObjectInt(18);
     }
+
+    /**
+     * Drops the cart as a item. The exact item dropped is defined by getItemDropped().
+     */
+    public void dropCartAsItem()
+    {
+        for(ItemStack item : getItemsDropped())
+        {
+            entityDropItem(item, 0);
+        }
+    }
+
+    /**
+     * Override this to define which items your cart drops when broken.
+     * This does not include items contained in the inventory,
+     * that is handled elsewhere.
+     * @return A list of items dropped.
+     */
+    public List<ItemStack> getItemsDropped()
+    {
+        List<ItemStack> items = new ArrayList<ItemStack>();
+        items.add(new ItemStack(Item.minecartEmpty));
+
+        switch(minecartType)
+        {
+            case 1:
+                items.add(new ItemStack(Block.chest));
+                break;
+            case 2:
+                items.add(new ItemStack(Block.stoneOvenIdle));
+                break;
+        }
+        return items;
+    }
+
+    /**
+     * This function returns an ItemStack that represents this cart.
+     * This should be an ItemStack that can be used by the player to place the cart.
+     * This is the item that was registered with the cart via the registerMinecart function,
+     * but is not necessary the item the cart drops when destroyed.
+     * @return An ItemStack that can be used to place the cart.
+     */
+    public ItemStack getCartItem()
+    {
+        return MinecartRegistry.getItemForCart(this);
+    }
+
+    /**
+     * Returns true if this cart is self propelled.
+     * @return True if powered.
+     */
+    public boolean isPoweredCart()
+    {
+        return minecartType == 2 && getClass() == EntityMinecart.class;
+    }
+
+    /**
+     * Returns true if this cart is a storage cart
+     * Some carts may have inventories but not be storage carts
+     * and some carts without inventories may be storage carts.
+     * @return True if this cart should be classified as a storage cart.
+     */
+    public boolean isStorageCart()
+    {
+        return minecartType == 1 && getClass() == EntityMinecart.class;
+    }
+
+    /**
+     * Returns true if this cart can be ridden by an Entity.
+     * @return True if this cart can be ridden.
+     */
+    public boolean canBeRidden()
+    {
+        if(minecartType == 0 && getClass() == EntityMinecart.class)
+        {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Returns true if this cart can currently use rails.
+     * This function is mainly used to gracefully detach a minecart from a rail.
+     * @return True if the minecart can use rails.
+     */
+    public boolean canUseRail()
+    {
+        return canUseRail;
+    }
+
+    /**
+     * Set whether the minecart can use rails.
+     * This function is mainly used to gracefully detach a minecart from a rail.
+     * @param use Whether the minecart can currently use rails.
+     */
+    public void setCanUseRail(boolean use)
+    {
+        canUseRail = use;
+    }
+
+    /**
+     * Return false if this cart should not call IRail.onMinecartPass() and should ignore Powered Rails.
+     * @return True if this cart should call IRail.onMinecartPass().
+     */
+    public boolean shouldDoRailFunctions()
+    {
+        return true;
+    }
+
+    /**
+     * Simply returns the minecartType variable.
+     * @return minecartType
+     */
+    public int getMinecartType()
+    {
+        return minecartType;
+    }
+
+    /**
+     * Gets the current global Minecart Collision handler if none
+     * is registered, returns null
+     * @return The collision handler or null
+     */
+    public static IMinecartCollisionHandler getCollisionHandler()
+    {
+        return collisionHandler;
+    }
+
+    /**
+     * Sets the global Minecart Collision handler, overwrites any
+     * that is currently set.
+     * @param handler The new handler
+     */
+    public static void setCollisionHandler(IMinecartCollisionHandler handler)
+    {
+        collisionHandler = handler;
+    }
+
+    /**
+     * Carts should return their drag factor here
+     * @return The drag rate.
+     */
+    protected double getDrag()
+    {
+        return riddenByEntity != null ? defaultDragRidden : defaultDragEmpty;
+    }
+
+    /**
+     * Moved to allow overrides.
+     * This code applies drag and updates push forces.
+     */
+    protected void applyDragAndPushForces()
+    {
+        if(isPoweredCart())
+        {
+            double d27 = MathHelper.sqrt_double(pushX * pushX + pushZ * pushZ);
+            if(d27 > 0.01D)
+            {
+                pushX /= d27;
+                pushZ /= d27;
+                double d29 = 0.04;
+                motionX *= 0.8D;
+                motionY *= 0.0D;
+                motionZ *= 0.8D;
+                motionX += pushX * d29;
+                motionZ += pushZ * d29;
+            }
+            else
+            {
+                motionX *= 0.9D;
+                motionY *= 0.0D;
+                motionZ *= 0.9D;
+            }
+        }
+        motionX *= getDrag();
+        motionY *= 0.0D;
+        motionZ *= getDrag();
+    }
+
+    /**
+     * Moved to allow overrides.
+     * This code updates push forces.
+     */
+    protected void updatePushForces()
+    {
+        if(isPoweredCart())
+        {
+            double push = MathHelper.sqrt_double(pushX * pushX + pushZ * pushZ);
+            if(push > 0.01D && motionX * motionX + motionZ * motionZ > 0.001D)
+            {
+                pushX /= push;
+                pushZ /= push;
+                if(pushX * motionX + pushZ * motionZ < 0.0D)
+                {
+                    pushX = 0.0D;
+                    pushZ = 0.0D;
+                }
+                else
+                {
+                    pushX = motionX;
+                    pushZ = motionZ;
+                }
+            }
+        }
+    }
+
+    /**
+     * Moved to allow overrides.
+     * This code handles minecart movement and speed capping when on a rail.
+     */
+    protected void moveMinecartOnRail(int i, int j, int k)
+    {
+        int id = worldObj.getBlockId(i, j, k);
+        if (!BlockRail.isRailBlock(id))
+        {
+                return;
+        }
+        float railMaxSpeed = ((BlockRail)Block.blocksList[id]).getRailMaxSpeed(worldObj, this, i, j, k);
+
+        double maxSpeed = Math.min(railMaxSpeed, getMaxSpeedRail());
+        double mX = motionX;
+        double mZ = motionZ;
+        if(riddenByEntity != null)
+        {
+            mX *= 0.75D;
+            mZ *= 0.75D;
+        }
+        if(mX < -maxSpeed) mX = -maxSpeed;
+        if(mX >  maxSpeed) mX =  maxSpeed;
+        if(mZ < -maxSpeed) mZ = -maxSpeed;
+        if(mZ >  maxSpeed) mZ =  maxSpeed;
+        moveEntity(mX, 0.0D, mZ);
+    }
+
+    /**
+     * Moved to allow overrides.
+     * This code handles minecart movement and speed capping when not on a rail.
+     */
+    protected void moveMinecartOffRail(int i, int j, int k)
+    {
+        double d2 = getMaxSpeedGround();
+        if(!onGround)
+        {
+            d2 = getMaxSpeedAirLateral();
+        }
+        if(motionX < -d2) motionX = -d2;
+        if(motionX >  d2) motionX =  d2;
+        if(motionZ < -d2) motionZ = -d2;
+        if(motionZ >  d2) motionZ =  d2;
+        double moveY = motionY;
+        if(getMaxSpeedAirVertical() > 0 && motionY > getMaxSpeedAirVertical())
+        {
+            moveY = getMaxSpeedAirVertical();
+            if(Math.abs(motionX) < 0.3f && Math.abs(motionZ) < 0.3f)
+            {
+                moveY = 0.15f;
+                motionY = moveY;
+            }
+        }
+        if(onGround)
+        {
+            motionX *= 0.5D;
+            motionY *= 0.5D;
+            motionZ *= 0.5D;
+        }
+        moveEntity(motionX, moveY, motionZ);
+        if(!onGround)
+        {
+            motionX *= getDragAir();
+            motionY *= getDragAir();
+            motionZ *= getDragAir();
+        }
+    }
+
+    /**
+     * Moved to allow overrides.
+     * This code applies fuel consumption.
+     */
+    protected void updateFuel()
+    {
+        if (fuel > 0) fuel--;
+        if (fuel <= 0) pushX = pushZ = 0.0D;
+        setMinecartPowered(fuel > 0);
+    }
+
+    /**
+     * Moved to allow overrides, This code handle slopes affecting velocity.
+     * @param metadata The blocks position metadata
+     */
+    protected void adjustSlopeVelocities(int metadata)
+    {
+        double acceleration = 0.0078125D;
+        if (metadata == 2)
+        {
+            motionX -= acceleration;
+        }
+        else if (metadata == 3)
+        {
+            motionX += acceleration;
+        }
+        else if (metadata == 4)
+        {
+            motionZ += acceleration;
+        }
+        else if (metadata == 5)
+        {
+            motionZ -= acceleration;
+        }
+    }
+
+    /**
+     * Getters/setters for physics variables
+     */
+
+    /**
+     * Returns the carts max speed.
+     * Carts going faster than 1.1 cause issues with chunk loading.
+     * Carts cant traverse slopes or corners at greater than 0.5 - 0.6.
+     * This value is compared with the rails max speed to determine
+     * the carts current max speed. A normal rails max speed is 0.4.
+     * @return Carts max speed.
+     */
+    public float getMaxSpeedRail()
+    {
+        return maxSpeedRail;
+    }
+
+    public void setMaxSpeedRail(float value)
+    {
+        maxSpeedRail = value;
+    }
+
+    public float getMaxSpeedGround()
+    {
+        return maxSpeedGround;
+    }
+
+    public void setMaxSpeedGround(float value)
+    {
+        maxSpeedGround = value;
+    }
+
+    public float getMaxSpeedAirLateral()
+    {
+        return maxSpeedAirLateral;
+    }
+
+    public void setMaxSpeedAirLateral(float value)
+    {
+        maxSpeedAirLateral = value;
+    }
+
+    public float getMaxSpeedAirVertical()
+    {
+        return maxSpeedAirVertical;
+    }
+
+    public void setMaxSpeedAirVertical(float value)
+    {
+        maxSpeedAirVertical = value;
+    }
+
+    public double getDragAir()
+    {
+        return dragAir;
+    }
+
+    public void setDragAir(double value)
+    {
+        dragAir = value;
+    }
 }
<commit_msg>Fix missed patch in EntityMinecart, #334<commit_after>--- ../src_base/minecraft/net/minecraft/entity/item/EntityMinecart.java
+++ ../src_work/minecraft/net/minecraft/entity/item/EntityMinecart.java
@@ -2,6 +2,8 @@
 
 import cpw.mods.fml.relauncher.Side;
 import cpw.mods.fml.relauncher.SideOnly;
+
+import java.util.ArrayList;
 import java.util.List;
 import net.minecraft.block.Block;
 import net.minecraft.block.BlockRail;
@@ -23,6 +25,11 @@
 import net.minecraft.world.World;
 import net.minecraft.world.WorldServer;
 
+import net.minecraftforge.common.IMinecartCollisionHandler;
+import net.minecraftforge.common.MinecartRegistry;
+import net.minecraftforge.common.MinecraftForge;
+import net.minecraftforge.event.entity.minecart.*;
+
 public class EntityMinecart extends Entity implements IInventory
 {
     /** Array of item stacks stored in minecart (for storage minecarts). */
@@ -52,6 +59,25 @@
     @SideOnly(Side.CLIENT)
     protected double velocityZ;
 
+    /* Forge: Minecart Compatibility Layer Integration. */
+    public static float defaultMaxSpeedRail = 0.4f;
+    public static float defaultMaxSpeedGround = 0.4f;
+    public static float defaultMaxSpeedAirLateral = 0.4f;
+    public static float defaultMaxSpeedAirVertical = -1f;
+    public static double defaultDragRidden = 0.996999979019165D;
+    public static double defaultDragEmpty = 0.9599999785423279D;
+    public static double defaultDragAir = 0.94999998807907104D;
+    protected boolean canUseRail = true;
+    protected boolean canBePushed = true;
+    private static IMinecartCollisionHandler collisionHandler = null;
+
+    /* Instance versions of the above physics properties */
+    protected float maxSpeedRail;
+    protected float maxSpeedGround;
+    protected float maxSpeedAirLateral;
+    protected float maxSpeedAirVertical;
+    protected double dragAir;
+
     public EntityMinecart(World par1World)
     {
         super(par1World);
@@ -63,6 +89,18 @@
         this.setSize(0.98F, 0.7F);
         this.yOffset = this.height / 2.0F;
         this.field_82344_g = par1World != null ? par1World.func_82735_a(this) : null;
+
+        maxSpeedRail = defaultMaxSpeedRail;
+        maxSpeedGround = defaultMaxSpeedGround;
+        maxSpeedAirLateral = defaultMaxSpeedAirLateral;
+        maxSpeedAirVertical = defaultMaxSpeedAirVertical;
+        dragAir = defaultDragAir;
+    }
+
+    public EntityMinecart(World world, int type)
+    {
+        this(world);
+        minecartType = type;
     }
 
     /**
@@ -88,6 +126,10 @@
      */
     public AxisAlignedBB getCollisionBox(Entity par1Entity)
     {
+        if (getCollisionHandler() != null)
+        {
+            return getCollisionHandler().getCollisionBox(this, par1Entity);
+        }
         return par1Entity.canBePushed() ? par1Entity.boundingBox : null;
     }
 
@@ -96,6 +138,10 @@
      */
     public AxisAlignedBB getBoundingBox()
     {
+        if (getCollisionHandler() != null)
+        {
+            return getCollisionHandler().getBoundingBox(this);
+        }
         return null;
     }
 
@@ -104,7 +150,7 @@
      */
     public boolean canBePushed()
     {
-        return true;
+        return canBePushed;
     }
 
     public EntityMinecart(World par1World, double par2, double par4, double par6, int par8)
@@ -159,48 +205,7 @@
                     }
 
                     this.setDead();
-                    this.dropItemWithOffset(Item.minecartEmpty.shiftedIndex, 1, 0.0F);
-
-                    if (this.minecartType == 1)
-                    {
-                        EntityMinecart var3 = this;
-
-                        for (int var4 = 0; var4 < var3.getSizeInventory(); ++var4)
-                        {
-                            ItemStack var5 = var3.getStackInSlot(var4);
-
-                            if (var5 != null)
-                            {
-                                float var6 = this.rand.nextFloat() * 0.8F + 0.1F;
-                                float var7 = this.rand.nextFloat() * 0.8F + 0.1F;
-                                float var8 = this.rand.nextFloat() * 0.8F + 0.1F;
-
-                                while (var5.stackSize > 0)
-                                {
-                                    int var9 = this.rand.nextInt(21) + 10;
-
-                                    if (var9 > var5.stackSize)
-                                    {
-                                        var9 = var5.stackSize;
-                                    }
-
-                                    var5.stackSize -= var9;
-                                    EntityItem var10 = new EntityItem(this.worldObj, this.posX + (double)var6, this.posY + (double)var7, this.posZ + (double)var8, new ItemStack(var5.itemID, var9, var5.getItemDamage()));
-                                    float var11 = 0.05F;
-                                    var10.motionX = (double)((float)this.rand.nextGaussian() * var11);
-                                    var10.motionY = (double)((float)this.rand.nextGaussian() * var11 + 0.2F);
-                                    var10.motionZ = (double)((float)this.rand.nextGaussian() * var11);
-                                    this.worldObj.spawnEntityInWorld(var10);
-                                }
-                            }
-                        }
-
-                        this.dropItemWithOffset(Block.chest.blockID, 1, 0.0F);
-                    }
-                    else if (this.minecartType == 2)
-                    {
-                        this.dropItemWithOffset(Block.stoneOvenIdle.blockID, 1, 0.0F);
-                    }
+                    dropCartAsItem();
                 }
 
                 return true;
@@ -318,7 +323,7 @@
             this.kill();
         }
 
-        if (this.isMinecartPowered() && this.rand.nextInt(4) == 0)
+        if (this.isMinecartPowered() && this.rand.nextInt(4) == 0 && minecartType == 2 && getClass() == EntityMinecart.class)
         {
             this.worldObj.spawnParticle("largesmoke", this.posX, this.posY + 0.8D, this.posZ, 0.0D, 0.0D, 0.0D);
         }
@@ -420,7 +425,7 @@
             {
                 this.fallDistance = 0.0F;
                 Vec3 var9 = this.func_70489_a(this.posX, this.posY, this.posZ);
-                int var10 = this.worldObj.getBlockMetadata(var45, var2, var47);
+                int var10 = ((BlockRail)Block.blocksList[var8]).getBasicRailMetadata(worldObj, this, var45, var2, var47);
                 this.posY = (double)var2;
                 boolean var11 = false;
                 boolean var12 = false;
@@ -441,25 +446,7 @@
                     this.posY = (double)(var2 + 1);
                 }
 
-                if (var10 == 2)
-                {
-                    this.motionX -= var6;
-                }
-
-                if (var10 == 3)
-                {
-                    this.motionX += var6;
-                }
-
-                if (var10 == 4)
-                {
-                    this.motionZ += var6;
-                }
-
-                if (var10 == 5)
-                {
-                    this.motionZ -= var6;
-                }
+                adjustSlopeVelocities(var10);
 
                 int[][] var13 = field_70500_g[var10];
                 double var14 = (double)(var13[1][0] - var13[0][0]);
@@ -492,7 +479,7 @@
                     }
                 }
 
-                if (var12)
+                if (var12 && shouldDoRailFunctions())
                 {
                     var24 = Math.sqrt(this.motionX * this.motionX + this.motionZ * this.motionZ);
 
@@ -540,36 +527,8 @@
                 this.posX = var26 + var14 * var24;
                 this.posZ = var28 + var16 * var24;
                 this.setPosition(this.posX, this.posY + (double)this.yOffset, this.posZ);
-                var34 = this.motionX;
-                var36 = this.motionZ;
-
-                if (this.riddenByEntity != null)
-                {
-                    var34 *= 0.75D;
-                    var36 *= 0.75D;
-                }
-
-                if (var34 < -var4)
-                {
-                    var34 = -var4;
-                }
-
-                if (var34 > var4)
-                {
-                    var34 = var4;
-                }
-
-                if (var36 < -var4)
-                {
-                    var36 = -var4;
-                }
-
-                if (var36 > var4)
-                {
-                    var36 = var4;
-                }
-
-                this.moveEntity(var34, 0.0D, var36);
+
+                moveMinecartOnRail(var45, var2, var47);
 
                 if (var13[0][1] != 0 && MathHelper.floor_double(this.posX) - var45 == var13[0][0] && MathHelper.floor_double(this.posZ) - var47 == var13[0][2])
                 {
@@ -580,42 +539,7 @@
                     this.setPosition(this.posX, this.posY + (double)var13[1][1], this.posZ);
                 }
 
-                if (this.riddenByEntity != null)
-                {
-                    this.motionX *= 0.996999979019165D;
-                    this.motionY *= 0.0D;
-                    this.motionZ *= 0.996999979019165D;
-                }
-                else
-                {
-                    if (this.minecartType == 2)
-                    {
-                        double var38 = this.pushX * this.pushX + this.pushZ * this.pushZ;
-
-                        if (var38 > 1.0E-4D)
-                        {
-                            var38 = (double)MathHelper.sqrt_double(var38);
-                            this.pushX /= var38;
-                            this.pushZ /= var38;
-                            double var40 = 0.04D;
-                            this.motionX *= 0.800000011920929D;
-                            this.motionY *= 0.0D;
-                            this.motionZ *= 0.800000011920929D;
-                            this.motionX += this.pushX * var40;
-                            this.motionZ += this.pushZ * var40;
-                        }
-                        else
-                        {
-                            this.motionX *= 0.8999999761581421D;
-                            this.motionY *= 0.0D;
-                            this.motionZ *= 0.8999999761581421D;
-                        }
-                    }
-
-                    this.motionX *= 0.9599999785423279D;
-                    this.motionY *= 0.0D;
-                    this.motionZ *= 0.9599999785423279D;
-                }
+                applyDragAndPushForces();
 
                 Vec3 var54 = this.func_70489_a(this.posX, this.posY, this.posZ);
 
@@ -645,30 +569,14 @@
 
                 double var41;
 
-                if (this.minecartType == 2)
-                {
-                    var41 = this.pushX * this.pushX + this.pushZ * this.pushZ;
-
-                    if (var41 > 1.0E-4D && this.motionX * this.motionX + this.motionZ * this.motionZ > 0.001D)
-                    {
-                        var41 = (double)MathHelper.sqrt_double(var41);
-                        this.pushX /= var41;
-                        this.pushZ /= var41;
-
-                        if (this.pushX * this.motionX + this.pushZ * this.motionZ < 0.0D)
-                        {
-                            this.pushX = 0.0D;
-                            this.pushZ = 0.0D;
-                        }
-                        else
-                        {
-                            this.pushX = this.motionX;
-                            this.pushZ = this.motionZ;
-                        }
-                    }
-                }
-
-                if (var11)
+                updatePushForces();
+
+                if(shouldDoRailFunctions())
+                {
+                    ((BlockRail)Block.blocksList[var8]).onMinecartPass(worldObj, this, var45, var2, var47);
+                }
+
+                if (var11 && shouldDoRailFunctions())
                 {
                     var41 = Math.sqrt(this.motionX * this.motionX + this.motionZ * this.motionZ);
 
@@ -704,41 +612,7 @@
             }
             else
             {
-                if (this.motionX < -var4)
-                {
-                    this.motionX = -var4;
-                }
-
-                if (this.motionX > var4)
-                {
-                    this.motionX = var4;
-                }
-
-                if (this.motionZ < -var4)
-                {
-                    this.motionZ = -var4;
-                }
-
-                if (this.motionZ > var4)
-                {
-                    this.motionZ = var4;
-                }
-
-                if (this.onGround)
-                {
-                    this.motionX *= 0.5D;
-                    this.motionY *= 0.5D;
-                    this.motionZ *= 0.5D;
-                }
-
-                this.moveEntity(this.motionX, this.motionY, this.motionZ);
-
-                if (!this.onGround)
-                {
-                    this.motionX *= 0.949999988079071D;
-                    this.motionY *= 0.949999988079071D;
-                    this.motionZ *= 0.949999988079071D;
-                }
+                moveMinecartOffRail(var45, var2, var47);
             }
 
             this.doBlockCollisions();
@@ -765,7 +639,18 @@
             }
 
             this.setRotation(this.rotationYaw, this.rotationPitch);
-            List var15 = this.worldObj.getEntitiesWithinAABBExcludingEntity(this, this.boundingBox.expand(0.20000000298023224D, 0.0D, 0.20000000298023224D));
+
+            AxisAlignedBB box = null;
+            if (getCollisionHandler() != null)
+            {
+                box = getCollisionHandler().getMinecartCollisionBox(this);
+            }
+            else
+            {
+                box = boundingBox.expand(0.2D, 0.0D, 0.2D);
+            }
+
+            List var15 = this.worldObj.getEntitiesWithinAABBExcludingEntity(this, box);
 
             if (var15 != null && !var15.isEmpty())
             {
@@ -790,17 +675,8 @@
                 this.riddenByEntity = null;
             }
 
-            if (this.fuel > 0)
-            {
-                --this.fuel;
-            }
-
-            if (this.fuel <= 0)
-            {
-                this.pushX = this.pushZ = 0.0D;
-            }
-
-            this.setMinecartPowered(this.fuel > 0);
+            updateFuel();
+            MinecraftForge.EVENT_BUS.post(new MinecartUpdateEvent(this, var45, var2, var47));
         }
     }
 
@@ -824,12 +700,7 @@
         }
         else
         {
-            int var13 = this.worldObj.getBlockMetadata(var9, var10, var11);
-
-            if (((BlockRail)Block.blocksList[var12]).isPowered())
-            {
-                var13 &= 7;
-            }
+            int var13 = ((BlockRail)Block.blocksList[var12]).getBasicRailMetadata(worldObj, this, var9, var10, var11);
 
             par3 = (double)var10;
 
@@ -875,13 +746,8 @@
 
         if (BlockRail.isRailBlock(var10))
         {
-            int var11 = this.worldObj.getBlockMetadata(var7, var8, var9);
+            int var11 = ((BlockRail)Block.blocksList[var10]).getBasicRailMetadata(worldObj, this, var7, var8, var9);
             par3 = (double)var8;
-
-            if (((BlockRail)Block.blocksList[var10]).isPowered())
-            {
-                var11 &= 7;
-            }
 
             if (var11 >= 2 && var11 <= 5)
             {
@@ -946,13 +812,14 @@
     {
         par1NBTTagCompound.setInteger("Type", this.minecartType);
 
-        if (this.minecartType == 2)
+        if (isPoweredCart())
         {
             par1NBTTagCompound.setDouble("PushX", this.pushX);
             par1NBTTagCompound.setDouble("PushZ", this.pushZ);
-            par1NBTTagCompound.setShort("Fuel", (short)this.fuel);
-        }
-        else if (this.minecartType == 1)
+            par1NBTTagCompound.setInteger("Fuel", this.fuel);
+        }
+
+        if (getSizeInventory() > 0)
         {
             NBTTagList var2 = new NBTTagList();
 
@@ -978,13 +845,21 @@
     {
         this.minecartType = par1NBTTagCompound.getInteger("Type");
 
-        if (this.minecartType == 2)
+        if (isPoweredCart())
         {
             this.pushX = par1NBTTagCompound.getDouble("PushX");
             this.pushZ = par1NBTTagCompound.getDouble("PushZ");
-            this.fuel = par1NBTTagCompound.getShort("Fuel");
-        }
-        else if (this.minecartType == 1)
+            try
+            {
+                this.fuel = par1NBTTagCompound.getInteger("Fuel");
+            }
+            catch (ClassCastException e)
+            {
+                this.fuel = par1NBTTagCompound.getShort("Fuel");
+            }
+        }
+
+        if (getSizeInventory() > 0)
         {
             NBTTagList var2 = par1NBTTagCompound.getTagList("Items");
             this.cargoItems = new ItemStack[this.getSizeInventory()];
@@ -1013,11 +888,17 @@
      */
     public void applyEntityCollision(Entity par1Entity)
     {
+        MinecraftForge.EVENT_BUS.post(new MinecartCollisionEvent(this, par1Entity));
+        if (getCollisionHandler() != null)
+        {
+            getCollisionHandler().onEntityCollision(this, par1Entity);
+            return;
+        }
         if (!this.worldObj.isRemote)
         {
             if (par1Entity != this.riddenByEntity)
             {
-                if (par1Entity instanceof EntityLiving && !(par1Entity instanceof EntityPlayer) && !(par1Entity instanceof EntityIronGolem) && this.minecartType == 0 && this.motionX * this.motionX + this.motionZ * this.motionZ > 0.01D && this.riddenByEntity == null && par1Entity.ridingEntity == null)
+                if (par1Entity instanceof EntityLiving && !(par1Entity instanceof EntityPlayer) && !(par1Entity instanceof EntityIronGolem) && canBeRidden() && this.motionX * this.motionX + this.motionZ * this.motionZ > 0.01D && this.riddenByEntity == null && par1Entity.ridingEntity == null)
                 {
                     par1Entity.mountEntity(this);
                 }
@@ -1063,7 +944,7 @@
                         double var18 = par1Entity.motionX + this.motionX;
                         double var20 = par1Entity.motionZ + this.motionZ;
 
-                        if (((EntityMinecart)par1Entity).minecartType == 2 && this.minecartType != 2)
+                        if (((EntityMinecart)par1Entity).isPoweredCart() && !isPoweredCart())
                         {
                             this.motionX *= 0.20000000298023224D;
                             this.motionZ *= 0.20000000298023224D;
@@ -1071,7 +952,7 @@
                             par1Entity.motionX *= 0.949999988079071D;
                             par1Entity.motionZ *= 0.949999988079071D;
                         }
-                        else if (((EntityMinecart)par1Entity).minecartType != 2 && this.minecartType == 2)
+                        else if (!((EntityMinecart)par1Entity).isPoweredCart() && isPoweredCart())
                         {
                             par1Entity.motionX *= 0.20000000298023224D;
                             par1Entity.motionZ *= 0.20000000298023224D;
@@ -1106,7 +987,7 @@
      */
     public int getSizeInventory()
     {
-        return 27;
+        return (minecartType == 1 && getClass() == EntityMinecart.class ? 27 : 0);
     }
 
     /**
@@ -1209,7 +1090,12 @@
      */
     public boolean interact(EntityPlayer par1EntityPlayer)
     {
-        if (this.minecartType == 0)
+        if (MinecraftForge.EVENT_BUS.post(new MinecartInteractEvent(this, par1EntityPlayer)))
+        {
+            return true;
+        }
+
+        if (canBeRidden())
         {
             if (this.riddenByEntity != null && this.riddenByEntity instanceof EntityPlayer && this.riddenByEntity != par1EntityPlayer)
             {
@@ -1221,14 +1107,14 @@
                 par1EntityPlayer.mountEntity(this);
             }
         }
-        else if (this.minecartType == 1)
+        else if (getSizeInventory() > 0)
         {
             if (!this.worldObj.isRemote)
             {
                 par1EntityPlayer.displayGUIChest(this);
             }
         }
-        else if (this.minecartType == 2)
+        else if (this.minecartType == 2 && getClass() == EntityMinecart.class)
         {
             ItemStack var2 = par1EntityPlayer.inventory.getCurrentItem();
 
@@ -1352,4 +1238,375 @@
     {
         return this.dataWatcher.getWatchableObjectInt(18);
     }
+
+    /**
+     * Drops the cart as a item. The exact item dropped is defined by getItemDropped().
+     */
+    public void dropCartAsItem()
+    {
+        for(ItemStack item : getItemsDropped())
+        {
+            entityDropItem(item, 0);
+        }
+    }
+
+    /**
+     * Override this to define which items your cart drops when broken.
+     * This does not include items contained in the inventory,
+     * that is handled elsewhere.
+     * @return A list of items dropped.
+     */
+    public List<ItemStack> getItemsDropped()
+    {
+        List<ItemStack> items = new ArrayList<ItemStack>();
+        items.add(new ItemStack(Item.minecartEmpty));
+
+        switch(minecartType)
+        {
+            case 1:
+                items.add(new ItemStack(Block.chest));
+                break;
+            case 2:
+                items.add(new ItemStack(Block.stoneOvenIdle));
+                break;
+        }
+        return items;
+    }
+
+    /**
+     * This function returns an ItemStack that represents this cart.
+     * This should be an ItemStack that can be used by the player to place the cart.
+     * This is the item that was registered with the cart via the registerMinecart function,
+     * but is not necessary the item the cart drops when destroyed.
+     * @return An ItemStack that can be used to place the cart.
+     */
+    public ItemStack getCartItem()
+    {
+        return MinecartRegistry.getItemForCart(this);
+    }
+
+    /**
+     * Returns true if this cart is self propelled.
+     * @return True if powered.
+     */
+    public boolean isPoweredCart()
+    {
+        return minecartType == 2 && getClass() == EntityMinecart.class;
+    }
+
+    /**
+     * Returns true if this cart is a storage cart
+     * Some carts may have inventories but not be storage carts
+     * and some carts without inventories may be storage carts.
+     * @return True if this cart should be classified as a storage cart.
+     */
+    public boolean isStorageCart()
+    {
+        return minecartType == 1 && getClass() == EntityMinecart.class;
+    }
+
+    /**
+     * Returns true if this cart can be ridden by an Entity.
+     * @return True if this cart can be ridden.
+     */
+    public boolean canBeRidden()
+    {
+        if(minecartType == 0 && getClass() == EntityMinecart.class)
+        {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Returns true if this cart can currently use rails.
+     * This function is mainly used to gracefully detach a minecart from a rail.
+     * @return True if the minecart can use rails.
+     */
+    public boolean canUseRail()
+    {
+        return canUseRail;
+    }
+
+    /**
+     * Set whether the minecart can use rails.
+     * This function is mainly used to gracefully detach a minecart from a rail.
+     * @param use Whether the minecart can currently use rails.
+     */
+    public void setCanUseRail(boolean use)
+    {
+        canUseRail = use;
+    }
+
+    /**
+     * Return false if this cart should not call IRail.onMinecartPass() and should ignore Powered Rails.
+     * @return True if this cart should call IRail.onMinecartPass().
+     */
+    public boolean shouldDoRailFunctions()
+    {
+        return true;
+    }
+
+    /**
+     * Simply returns the minecartType variable.
+     * @return minecartType
+     */
+    public int getMinecartType()
+    {
+        return minecartType;
+    }
+
+    /**
+     * Gets the current global Minecart Collision handler if none
+     * is registered, returns null
+     * @return The collision handler or null
+     */
+    public static IMinecartCollisionHandler getCollisionHandler()
+    {
+        return collisionHandler;
+    }
+
+    /**
+     * Sets the global Minecart Collision handler, overwrites any
+     * that is currently set.
+     * @param handler The new handler
+     */
+    public static void setCollisionHandler(IMinecartCollisionHandler handler)
+    {
+        collisionHandler = handler;
+    }
+
+    /**
+     * Carts should return their drag factor here
+     * @return The drag rate.
+     */
+    protected double getDrag()
+    {
+        return riddenByEntity != null ? defaultDragRidden : defaultDragEmpty;
+    }
+
+    /**
+     * Moved to allow overrides.
+     * This code applies drag and updates push forces.
+     */
+    protected void applyDragAndPushForces()
+    {
+        if(isPoweredCart())
+        {
+            double d27 = MathHelper.sqrt_double(pushX * pushX + pushZ * pushZ);
+            if(d27 > 0.01D)
+            {
+                pushX /= d27;
+                pushZ /= d27;
+                double d29 = 0.04;
+                motionX *= 0.8D;
+                motionY *= 0.0D;
+                motionZ *= 0.8D;
+                motionX += pushX * d29;
+                motionZ += pushZ * d29;
+            }
+            else
+            {
+                motionX *= 0.9D;
+                motionY *= 0.0D;
+                motionZ *= 0.9D;
+            }
+        }
+        motionX *= getDrag();
+        motionY *= 0.0D;
+        motionZ *= getDrag();
+    }
+
+    /**
+     * Moved to allow overrides.
+     * This code updates push forces.
+     */
+    protected void updatePushForces()
+    {
+        if(isPoweredCart())
+        {
+            double push = MathHelper.sqrt_double(pushX * pushX + pushZ * pushZ);
+            if(push > 0.01D && motionX * motionX + motionZ * motionZ > 0.001D)
+            {
+                pushX /= push;
+                pushZ /= push;
+                if(pushX * motionX + pushZ * motionZ < 0.0D)
+                {
+                    pushX = 0.0D;
+                    pushZ = 0.0D;
+                }
+                else
+                {
+                    pushX = motionX;
+                    pushZ = motionZ;
+                }
+            }
+        }
+    }
+
+    /**
+     * Moved to allow overrides.
+     * This code handles minecart movement and speed capping when on a rail.
+     */
+    protected void moveMinecartOnRail(int i, int j, int k)
+    {
+        int id = worldObj.getBlockId(i, j, k);
+        if (!BlockRail.isRailBlock(id))
+        {
+                return;
+        }
+        float railMaxSpeed = ((BlockRail)Block.blocksList[id]).getRailMaxSpeed(worldObj, this, i, j, k);
+
+        double maxSpeed = Math.min(railMaxSpeed, getMaxSpeedRail());
+        double mX = motionX;
+        double mZ = motionZ;
+        if(riddenByEntity != null)
+        {
+            mX *= 0.75D;
+            mZ *= 0.75D;
+        }
+        if(mX < -maxSpeed) mX = -maxSpeed;
+        if(mX >  maxSpeed) mX =  maxSpeed;
+        if(mZ < -maxSpeed) mZ = -maxSpeed;
+        if(mZ >  maxSpeed) mZ =  maxSpeed;
+        moveEntity(mX, 0.0D, mZ);
+    }
+
+    /**
+     * Moved to allow overrides.
+     * This code handles minecart movement and speed capping when not on a rail.
+     */
+    protected void moveMinecartOffRail(int i, int j, int k)
+    {
+        double d2 = getMaxSpeedGround();
+        if(!onGround)
+        {
+            d2 = getMaxSpeedAirLateral();
+        }
+        if(motionX < -d2) motionX = -d2;
+        if(motionX >  d2) motionX =  d2;
+        if(motionZ < -d2) motionZ = -d2;
+        if(motionZ >  d2) motionZ =  d2;
+        double moveY = motionY;
+        if(getMaxSpeedAirVertical() > 0 && motionY > getMaxSpeedAirVertical())
+        {
+            moveY = getMaxSpeedAirVertical();
+            if(Math.abs(motionX) < 0.3f && Math.abs(motionZ) < 0.3f)
+            {
+                moveY = 0.15f;
+                motionY = moveY;
+            }
+        }
+        if(onGround)
+        {
+            motionX *= 0.5D;
+            motionY *= 0.5D;
+            motionZ *= 0.5D;
+        }
+        moveEntity(motionX, moveY, motionZ);
+        if(!onGround)
+        {
+            motionX *= getDragAir();
+            motionY *= getDragAir();
+            motionZ *= getDragAir();
+        }
+    }
+
+    /**
+     * Moved to allow overrides.
+     * This code applies fuel consumption.
+     */
+    protected void updateFuel()
+    {
+        if (fuel > 0) fuel--;
+        if (fuel <= 0) pushX = pushZ = 0.0D;
+        setMinecartPowered(fuel > 0);
+    }
+
+    /**
+     * Moved to allow overrides, This code handle slopes affecting velocity.
+     * @param metadata The blocks position metadata
+     */
+    protected void adjustSlopeVelocities(int metadata)
+    {
+        double acceleration = 0.0078125D;
+        if (metadata == 2)
+        {
+            motionX -= acceleration;
+        }
+        else if (metadata == 3)
+        {
+            motionX += acceleration;
+        }
+        else if (metadata == 4)
+        {
+            motionZ += acceleration;
+        }
+        else if (metadata == 5)
+        {
+            motionZ -= acceleration;
+        }
+    }
+
+    /**
+     * Getters/setters for physics variables
+     */
+
+    /**
+     * Returns the carts max speed.
+     * Carts going faster than 1.1 cause issues with chunk loading.
+     * Carts cant traverse slopes or corners at greater than 0.5 - 0.6.
+     * This value is compared with the rails max speed to determine
+     * the carts current max speed. A normal rails max speed is 0.4.
+     * @return Carts max speed.
+     */
+    public float getMaxSpeedRail()
+    {
+        return maxSpeedRail;
+    }
+
+    public void setMaxSpeedRail(float value)
+    {
+        maxSpeedRail = value;
+    }
+
+    public float getMaxSpeedGround()
+    {
+        return maxSpeedGround;
+    }
+
+    public void setMaxSpeedGround(float value)
+    {
+        maxSpeedGround = value;
+    }
+
+    public float getMaxSpeedAirLateral()
+    {
+        return maxSpeedAirLateral;
+    }
+
+    public void setMaxSpeedAirLateral(float value)
+    {
+        maxSpeedAirLateral = value;
+    }
+
+    public float getMaxSpeedAirVertical()
+    {
+        return maxSpeedAirVertical;
+    }
+
+    public void setMaxSpeedAirVertical(float value)
+    {
+        maxSpeedAirVertical = value;
+    }
+
+    public double getDragAir()
+    {
+        return dragAir;
+    }
+
+    public void setDragAir(double value)
+    {
+        dragAir = value;
+    }
 }

{'fraction_non_alphanumeric': 0.1219403795822291, 'fraction_numerical': 0.041480798669980744, 'mean_word_length': 1.3831790365604895, 'pattern_counts': {'":': 0, '<': 41, '<?xml version=': 0, '>': 65, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 12, 'word_list_counts': {'cursed_substrings.json': 30, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6347025', 'n_tokens_mistral': 20523, 'n_tokens_neox': 18369, 'n_words': 3628}