001package net.minecraft.entity.item;
002
003import cpw.mods.fml.relauncher.Side;
004import cpw.mods.fml.relauncher.SideOnly;
005import java.util.List;
006import net.minecraft.block.Block;
007import net.minecraft.block.BlockRailBase;
008import net.minecraft.entity.Entity;
009import net.minecraft.entity.EntityLiving;
010import net.minecraft.entity.ai.EntityMinecartMobSpawner;
011import net.minecraft.entity.monster.EntityIronGolem;
012import net.minecraft.entity.player.EntityPlayer;
013import net.minecraft.item.Item;
014import net.minecraft.item.ItemStack;
015import net.minecraft.nbt.NBTTagCompound;
016import net.minecraft.server.MinecraftServer;
017import net.minecraft.server.gui.IUpdatePlayerListBox;
018import net.minecraft.util.AxisAlignedBB;
019import net.minecraft.util.DamageSource;
020import net.minecraft.util.MathHelper;
021import net.minecraft.util.Vec3;
022import net.minecraft.world.World;
023import net.minecraft.world.WorldServer;
024import net.minecraftforge.common.IMinecartCollisionHandler;
025import net.minecraftforge.common.MinecraftForge;
026import net.minecraftforge.event.entity.minecart.MinecartCollisionEvent;
027import net.minecraftforge.event.entity.minecart.MinecartUpdateEvent;
028
029public abstract class EntityMinecart extends Entity
030{
031    protected boolean isInReverse;
032    protected final IUpdatePlayerListBox field_82344_g;
033    protected String entityName;
034
035    /** Minecart rotational logic matrix */
036    protected 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}}};
037
038    /** appears to be the progress of the turn */
039    protected int turnProgress;
040    protected double minecartX;
041    protected double minecartY;
042    protected double minecartZ;
043    protected double minecartYaw;
044    protected double minecartPitch;
045    @SideOnly(Side.CLIENT)
046    protected double velocityX;
047    @SideOnly(Side.CLIENT)
048    protected double velocityY;
049    @SideOnly(Side.CLIENT)
050    protected double velocityZ;
051
052    /* Forge: Minecart Compatibility Layer Integration. */
053    public static float defaultMaxSpeedAirLateral = 0.4f;
054    public static float defaultMaxSpeedAirVertical = -1f;
055    public static double defaultDragAir = 0.94999998807907104D;
056    protected boolean canUseRail = true;
057    protected boolean canBePushed = true;
058    private static IMinecartCollisionHandler collisionHandler = null;
059
060    /* Instance versions of the above physics properties */
061    private float currentSpeedRail = getMaxCartSpeedOnRail();
062    protected float maxSpeedAirLateral = defaultMaxSpeedAirLateral;
063    protected float maxSpeedAirVertical = defaultMaxSpeedAirVertical;
064    protected double dragAir = defaultDragAir;
065
066    public EntityMinecart(World par1World)
067    {
068        super(par1World);
069        this.isInReverse = false;
070        this.preventEntitySpawning = true;
071        this.setSize(0.98F, 0.7F);
072        this.yOffset = this.height / 2.0F;
073        this.field_82344_g = par1World != null ? par1World.func_82735_a(this) : null;
074    }
075
076    /**
077     * Creates a new minecart of the specified type in the specified location in the given world. par0World - world to
078     * create the minecart in, double par1,par3,par5 represent x,y,z respectively. int par7 specifies the type: 1 for
079     * MinecartChest, 2 for MinecartFurnace, 3 for MinecartTNT, 4 for MinecartMobSpawner, 5 for MinecartHopper and 0 for
080     * a standard empty minecart
081     */
082    public static EntityMinecart createMinecart(World par0World, double par1, double par3, double par5, int par7)
083    {
084        switch (par7)
085        {
086            case 1:
087                return new EntityMinecartChest(par0World, par1, par3, par5);
088            case 2:
089                return new EntityMinecartFurnace(par0World, par1, par3, par5);
090            case 3:
091                return new EntityMinecartTNT(par0World, par1, par3, par5);
092            case 4:
093                return new EntityMinecartMobSpawner(par0World, par1, par3, par5);
094            case 5:
095                return new EntityMinecartHopper(par0World, par1, par3, par5);
096            default:
097                return new EntityMinecartEmpty(par0World, par1, par3, par5);
098        }
099    }
100
101    /**
102     * returns if this entity triggers Block.onEntityWalking on the blocks they walk on. used for spiders and wolves to
103     * prevent them from trampling crops
104     */
105    protected boolean canTriggerWalking()
106    {
107        return false;
108    }
109
110    protected void entityInit()
111    {
112        this.dataWatcher.addObject(17, new Integer(0));
113        this.dataWatcher.addObject(18, new Integer(1));
114        this.dataWatcher.addObject(19, new Integer(0));
115        this.dataWatcher.addObject(20, new Integer(0));
116        this.dataWatcher.addObject(21, new Integer(6));
117        this.dataWatcher.addObject(22, Byte.valueOf((byte)0));
118    }
119
120    /**
121     * Returns a boundingBox used to collide the entity with other entities and blocks. This enables the entity to be
122     * pushable on contact, like boats or minecarts.
123     */
124    public AxisAlignedBB getCollisionBox(Entity par1Entity)
125    {
126        if (getCollisionHandler() != null)
127        {
128            return getCollisionHandler().getCollisionBox(this, par1Entity);
129        }
130        return par1Entity.canBePushed() ? par1Entity.boundingBox : null;
131    }
132
133    /**
134     * returns the bounding box for this entity
135     */
136    public AxisAlignedBB getBoundingBox()
137    {
138        if (getCollisionHandler() != null)
139        {
140            return getCollisionHandler().getBoundingBox(this);
141        }
142        return null;
143    }
144
145    /**
146     * Returns true if this entity should push and be pushed by other entities when colliding.
147     */
148    public boolean canBePushed()
149    {
150        return canBePushed;
151    }
152
153    public EntityMinecart(World par1World, double par2, double par4, double par6)
154    {
155        this(par1World);
156        this.setPosition(par2, par4 + (double)this.yOffset, par6);
157        this.motionX = 0.0D;
158        this.motionY = 0.0D;
159        this.motionZ = 0.0D;
160        this.prevPosX = par2;
161        this.prevPosY = par4;
162        this.prevPosZ = par6;
163    }
164
165    /**
166     * Returns the Y offset from the entity's position for any entity riding this one.
167     */
168    public double getMountedYOffset()
169    {
170        return (double)this.height * 0.0D - 0.30000001192092896D;
171    }
172
173    /**
174     * Called when the entity is attacked.
175     */
176    public boolean attackEntityFrom(DamageSource par1DamageSource, int par2)
177    {
178        if (!this.worldObj.isRemote && !this.isDead)
179        {
180            if (this.isEntityInvulnerable())
181            {
182                return false;
183            }
184            else
185            {
186                this.setRollingDirection(-this.getRollingDirection());
187                this.setRollingAmplitude(10);
188                this.setBeenAttacked();
189                this.setDamage(this.getDamage() + par2 * 10);
190                boolean flag = par1DamageSource.getEntity() instanceof EntityPlayer && ((EntityPlayer)par1DamageSource.getEntity()).capabilities.isCreativeMode;
191
192                if (flag || this.getDamage() > 40)
193                {
194                    if (this.riddenByEntity != null)
195                    {
196                        this.riddenByEntity.mountEntity(this);
197                    }
198
199                    if (flag && !this.isInvNameLocalized())
200                    {
201                        this.setDead();
202                    }
203                    else
204                    {
205                        this.killMinecart(par1DamageSource);
206                    }
207                }
208
209                return true;
210            }
211        }
212        else
213        {
214            return true;
215        }
216    }
217
218    public void killMinecart(DamageSource par1DamageSource)
219    {
220        this.setDead();
221        ItemStack itemstack = new ItemStack(Item.minecartEmpty, 1);
222
223        if (this.entityName != null)
224        {
225            itemstack.setItemName(this.entityName);
226        }
227
228        this.entityDropItem(itemstack, 0.0F);
229    }
230
231    @SideOnly(Side.CLIENT)
232
233    /**
234     * Setups the entity to do the hurt animation. Only used by packets in multiplayer.
235     */
236    public void performHurtAnimation()
237    {
238        this.setRollingDirection(-this.getRollingDirection());
239        this.setRollingAmplitude(10);
240        this.setDamage(this.getDamage() + this.getDamage() * 10);
241    }
242
243    /**
244     * Returns true if other Entities should be prevented from moving through this Entity.
245     */
246    public boolean canBeCollidedWith()
247    {
248        return !this.isDead;
249    }
250
251    /**
252     * Will get destroyed next tick.
253     */
254    public void setDead()
255    {
256        super.setDead();
257
258        if (this.field_82344_g != null)
259        {
260            this.field_82344_g.update();
261        }
262    }
263
264    /**
265     * Called to update the entity's position/logic.
266     */
267    public void onUpdate()
268    {
269        if (this.field_82344_g != null)
270        {
271            this.field_82344_g.update();
272        }
273
274        if (this.getRollingAmplitude() > 0)
275        {
276            this.setRollingAmplitude(this.getRollingAmplitude() - 1);
277        }
278
279        if (this.getDamage() > 0)
280        {
281            this.setDamage(this.getDamage() - 1);
282        }
283
284        if (this.posY < -64.0D)
285        {
286            this.kill();
287        }
288
289        int i;
290
291        if (!this.worldObj.isRemote && this.worldObj instanceof WorldServer)
292        {
293            this.worldObj.theProfiler.startSection("portal");
294            MinecraftServer minecraftserver = ((WorldServer)this.worldObj).getMinecraftServer();
295            i = this.getMaxInPortalTime();
296
297            if (this.inPortal)
298            {
299                if (minecraftserver.getAllowNether())
300                {
301                    if (this.ridingEntity == null && this.timeInPortal++ >= i)
302                    {
303                        this.timeInPortal = i;
304                        this.timeUntilPortal = this.getPortalCooldown();
305                        byte b0;
306
307                        if (this.worldObj.provider.dimensionId == -1)
308                        {
309                            b0 = 0;
310                        }
311                        else
312                        {
313                            b0 = -1;
314                        }
315
316                        this.travelToDimension(b0);
317                    }
318
319                    this.inPortal = false;
320                }
321            }
322            else
323            {
324                if (this.timeInPortal > 0)
325                {
326                    this.timeInPortal -= 4;
327                }
328
329                if (this.timeInPortal < 0)
330                {
331                    this.timeInPortal = 0;
332                }
333            }
334
335            if (this.timeUntilPortal > 0)
336            {
337                --this.timeUntilPortal;
338            }
339
340            this.worldObj.theProfiler.endSection();
341        }
342
343        if (this.worldObj.isRemote)
344        {
345            if (this.turnProgress > 0)
346            {
347                double d0 = this.posX + (this.minecartX - this.posX) / (double)this.turnProgress;
348                double d1 = this.posY + (this.minecartY - this.posY) / (double)this.turnProgress;
349                double d2 = this.posZ + (this.minecartZ - this.posZ) / (double)this.turnProgress;
350                double d3 = MathHelper.wrapAngleTo180_double(this.minecartYaw - (double)this.rotationYaw);
351                this.rotationYaw = (float)((double)this.rotationYaw + d3 / (double)this.turnProgress);
352                this.rotationPitch = (float)((double)this.rotationPitch + (this.minecartPitch - (double)this.rotationPitch) / (double)this.turnProgress);
353                --this.turnProgress;
354                this.setPosition(d0, d1, d2);
355                this.setRotation(this.rotationYaw, this.rotationPitch);
356            }
357            else
358            {
359                this.setPosition(this.posX, this.posY, this.posZ);
360                this.setRotation(this.rotationYaw, this.rotationPitch);
361            }
362        }
363        else
364        {
365            this.prevPosX = this.posX;
366            this.prevPosY = this.posY;
367            this.prevPosZ = this.posZ;
368            this.motionY -= 0.03999999910593033D;
369            int j = MathHelper.floor_double(this.posX);
370            i = MathHelper.floor_double(this.posY);
371            int k = MathHelper.floor_double(this.posZ);
372
373            if (BlockRailBase.isRailBlockAt(this.worldObj, j, i - 1, k))
374            {
375                --i;
376            }
377
378            double d4 = 0.4D;
379            double d5 = 0.0078125D;
380            int l = this.worldObj.getBlockId(j, i, k);
381
382            if (canUseRail() && BlockRailBase.isRailBlock(l))
383            {
384                BlockRailBase rail = (BlockRailBase)Block.blocksList[l];
385                float railMaxSpeed = rail.getRailMaxSpeed(worldObj, this, j, i, k);
386                double maxSpeed = Math.min(railMaxSpeed, getCurrentCartSpeedCapOnRail());
387                int i1 = rail.getBasicRailMetadata(worldObj, this, j, i, k);
388                this.updateOnTrack(j, i, k, maxSpeed, getSlopeAdjustment(), l, i1);
389                if (l == Block.railActivator.blockID)
390                {
391                    this.onActivatorRailPass(j, i, k, (worldObj.getBlockMetadata(j, i, k) & 8) != 0);
392                }
393            }
394            else
395            {
396                this.func_94088_b(onGround ? d4 : getMaxSpeedAirLateral());
397            }
398
399            this.doBlockCollisions();
400            this.rotationPitch = 0.0F;
401            double d6 = this.prevPosX - this.posX;
402            double d7 = this.prevPosZ - this.posZ;
403
404            if (d6 * d6 + d7 * d7 > 0.001D)
405            {
406                this.rotationYaw = (float)(Math.atan2(d7, d6) * 180.0D / Math.PI);
407
408                if (this.isInReverse)
409                {
410                    this.rotationYaw += 180.0F;
411                }
412            }
413
414            double d8 = (double)MathHelper.wrapAngleTo180_float(this.rotationYaw - this.prevRotationYaw);
415
416            if (d8 < -170.0D || d8 >= 170.0D)
417            {
418                this.rotationYaw += 180.0F;
419                this.isInReverse = !this.isInReverse;
420            }
421
422            this.setRotation(this.rotationYaw, this.rotationPitch);
423
424            AxisAlignedBB box;
425            if (getCollisionHandler() != null)
426            {
427                box = getCollisionHandler().getMinecartCollisionBox(this);
428            }
429            else
430            {
431                box = boundingBox.expand(0.2D, 0.0D, 0.2D);
432            }
433
434            List list = this.worldObj.getEntitiesWithinAABBExcludingEntity(this, box);
435
436            if (list != null && !list.isEmpty())
437            {
438                for (int j1 = 0; j1 < list.size(); ++j1)
439                {
440                    Entity entity = (Entity)list.get(j1);
441
442                    if (entity != this.riddenByEntity && entity.canBePushed() && entity instanceof EntityMinecart)
443                    {
444                        entity.applyEntityCollision(this);
445                    }
446                }
447            }
448
449            if (this.riddenByEntity != null && this.riddenByEntity.isDead)
450            {
451                if (this.riddenByEntity.ridingEntity == this)
452                {
453                    this.riddenByEntity.ridingEntity = null;
454                }
455
456                this.riddenByEntity = null;
457            }
458
459            MinecraftForge.EVENT_BUS.post(new MinecartUpdateEvent(this, j, i, k));
460        }
461    }
462
463    /**
464     * Called every tick the minecart is on an activator rail.
465     */
466    public void onActivatorRailPass(int par1, int par2, int par3, boolean par4) {}
467
468    protected void func_94088_b(double par1)
469    {
470        if (this.motionX < -par1)
471        {
472            this.motionX = -par1;
473        }
474
475        if (this.motionX > par1)
476        {
477            this.motionX = par1;
478        }
479
480        if (this.motionZ < -par1)
481        {
482            this.motionZ = -par1;
483        }
484
485        if (this.motionZ > par1)
486        {
487            this.motionZ = par1;
488        }
489
490        double moveY = motionY;
491        if(getMaxSpeedAirVertical() > 0 && motionY > getMaxSpeedAirVertical())
492        {
493            moveY = getMaxSpeedAirVertical();
494            if(Math.abs(motionX) < 0.3f && Math.abs(motionZ) < 0.3f)
495            {
496                moveY = 0.15f;
497                motionY = moveY;
498            }
499        }
500
501        if (this.onGround)
502        {
503            this.motionX *= 0.5D;
504            this.motionY *= 0.5D;
505            this.motionZ *= 0.5D;
506        }
507
508        this.moveEntity(this.motionX, moveY, this.motionZ);
509
510        if (!this.onGround)
511        {
512            this.motionX *= getDragAir();
513            this.motionY *= getDragAir();
514            this.motionZ *= getDragAir();
515        }
516    }
517
518    protected void updateOnTrack(int par1, int par2, int par3, double par4, double par6, int par8, int par9)
519    {
520        this.fallDistance = 0.0F;
521        Vec3 vec3 = this.func_70489_a(this.posX, this.posY, this.posZ);
522        this.posY = (double)par2;
523        boolean flag = false;
524        boolean flag1 = false;
525
526        if (par8 == Block.railPowered.blockID)
527        {
528            flag = (worldObj.getBlockMetadata(par1, par2, par3) & 8) != 0;
529            flag1 = !flag;
530        }
531
532        if (((BlockRailBase)Block.blocksList[par8]).isPowered())
533        {
534            par9 &= 7;
535        }
536
537        if (par9 >= 2 && par9 <= 5)
538        {
539            this.posY = (double)(par2 + 1);
540        }
541
542        if (par9 == 2)
543        {
544            this.motionX -= par6;
545        }
546
547        if (par9 == 3)
548        {
549            this.motionX += par6;
550        }
551
552        if (par9 == 4)
553        {
554            this.motionZ += par6;
555        }
556
557        if (par9 == 5)
558        {
559            this.motionZ -= par6;
560        }
561
562        int[][] aint = matrix[par9];
563        double d2 = (double)(aint[1][0] - aint[0][0]);
564        double d3 = (double)(aint[1][2] - aint[0][2]);
565        double d4 = Math.sqrt(d2 * d2 + d3 * d3);
566        double d5 = this.motionX * d2 + this.motionZ * d3;
567
568        if (d5 < 0.0D)
569        {
570            d2 = -d2;
571            d3 = -d3;
572        }
573
574        double d6 = Math.sqrt(this.motionX * this.motionX + this.motionZ * this.motionZ);
575
576        if (d6 > 2.0D)
577        {
578            d6 = 2.0D;
579        }
580
581        this.motionX = d6 * d2 / d4;
582        this.motionZ = d6 * d3 / d4;
583        double d7;
584        double d8;
585
586        if (this.riddenByEntity != null)
587        {
588            d7 = this.riddenByEntity.motionX * this.riddenByEntity.motionX + this.riddenByEntity.motionZ * this.riddenByEntity.motionZ;
589            d8 = this.motionX * this.motionX + this.motionZ * this.motionZ;
590
591            if (d7 > 1.0E-4D && d8 < 0.01D)
592            {
593                this.motionX += this.riddenByEntity.motionX * 0.1D;
594                this.motionZ += this.riddenByEntity.motionZ * 0.1D;
595                flag1 = false;
596            }
597        }
598
599        if (flag1 && shouldDoRailFunctions())
600        {
601            d7 = Math.sqrt(this.motionX * this.motionX + this.motionZ * this.motionZ);
602
603            if (d7 < 0.03D)
604            {
605                this.motionX *= 0.0D;
606                this.motionY *= 0.0D;
607                this.motionZ *= 0.0D;
608            }
609            else
610            {
611                this.motionX *= 0.5D;
612                this.motionY *= 0.0D;
613                this.motionZ *= 0.5D;
614            }
615        }
616
617        d7 = 0.0D;
618        d8 = (double)par1 + 0.5D + (double)aint[0][0] * 0.5D;
619        double d9 = (double)par3 + 0.5D + (double)aint[0][2] * 0.5D;
620        double d10 = (double)par1 + 0.5D + (double)aint[1][0] * 0.5D;
621        double d11 = (double)par3 + 0.5D + (double)aint[1][2] * 0.5D;
622        d2 = d10 - d8;
623        d3 = d11 - d9;
624        double d12;
625        double d13;
626
627        if (d2 == 0.0D)
628        {
629            this.posX = (double)par1 + 0.5D;
630            d7 = this.posZ - (double)par3;
631        }
632        else if (d3 == 0.0D)
633        {
634            this.posZ = (double)par3 + 0.5D;
635            d7 = this.posX - (double)par1;
636        }
637        else
638        {
639            d12 = this.posX - d8;
640            d13 = this.posZ - d9;
641            d7 = (d12 * d2 + d13 * d3) * 2.0D;
642        }
643
644        this.posX = d8 + d2 * d7;
645        this.posZ = d9 + d3 * d7;
646        this.setPosition(this.posX, this.posY + (double)this.yOffset, this.posZ);
647
648        moveMinecartOnRail(par1, par2, par3, par4);
649
650        if (aint[0][1] != 0 && MathHelper.floor_double(this.posX) - par1 == aint[0][0] && MathHelper.floor_double(this.posZ) - par3 == aint[0][2])
651        {
652            this.setPosition(this.posX, this.posY + (double)aint[0][1], this.posZ);
653        }
654        else if (aint[1][1] != 0 && MathHelper.floor_double(this.posX) - par1 == aint[1][0] && MathHelper.floor_double(this.posZ) - par3 == aint[1][2])
655        {
656            this.setPosition(this.posX, this.posY + (double)aint[1][1], this.posZ);
657        }
658
659        this.applyDrag();
660        Vec3 vec31 = this.func_70489_a(this.posX, this.posY, this.posZ);
661
662        if (vec31 != null && vec3 != null)
663        {
664            double d14 = (vec3.yCoord - vec31.yCoord) * 0.05D;
665            d6 = Math.sqrt(this.motionX * this.motionX + this.motionZ * this.motionZ);
666
667            if (d6 > 0.0D)
668            {
669                this.motionX = this.motionX / d6 * (d6 + d14);
670                this.motionZ = this.motionZ / d6 * (d6 + d14);
671            }
672
673            this.setPosition(this.posX, vec31.yCoord, this.posZ);
674        }
675
676        int j1 = MathHelper.floor_double(this.posX);
677        int k1 = MathHelper.floor_double(this.posZ);
678
679        if (j1 != par1 || k1 != par3)
680        {
681            d6 = Math.sqrt(this.motionX * this.motionX + this.motionZ * this.motionZ);
682            this.motionX = d6 * (double)(j1 - par1);
683            this.motionZ = d6 * (double)(k1 - par3);
684        }
685
686        if(shouldDoRailFunctions())
687        {
688            ((BlockRailBase)Block.blocksList[par8]).onMinecartPass(worldObj, this, par1, par2, par3);
689        }
690
691        if (flag && shouldDoRailFunctions())
692        {
693            double d15 = Math.sqrt(this.motionX * this.motionX + this.motionZ * this.motionZ);
694
695            if (d15 > 0.01D)
696            {
697                double d16 = 0.06D;
698                this.motionX += this.motionX / d15 * d16;
699                this.motionZ += this.motionZ / d15 * d16;
700            }
701            else if (par9 == 1)
702            {
703                if (this.worldObj.isBlockNormalCube(par1 - 1, par2, par3))
704                {
705                    this.motionX = 0.02D;
706                }
707                else if (this.worldObj.isBlockNormalCube(par1 + 1, par2, par3))
708                {
709                    this.motionX = -0.02D;
710                }
711            }
712            else if (par9 == 0)
713            {
714                if (this.worldObj.isBlockNormalCube(par1, par2, par3 - 1))
715                {
716                    this.motionZ = 0.02D;
717                }
718                else if (this.worldObj.isBlockNormalCube(par1, par2, par3 + 1))
719                {
720                    this.motionZ = -0.02D;
721                }
722            }
723        }
724    }
725
726    protected void applyDrag()
727    {
728        if (this.riddenByEntity != null)
729        {
730            this.motionX *= 0.996999979019165D;
731            this.motionY *= 0.0D;
732            this.motionZ *= 0.996999979019165D;
733        }
734        else
735        {
736            this.motionX *= 0.9599999785423279D;
737            this.motionY *= 0.0D;
738            this.motionZ *= 0.9599999785423279D;
739        }
740    }
741
742    @SideOnly(Side.CLIENT)
743    public Vec3 func_70495_a(double par1, double par3, double par5, double par7)
744    {
745        int i = MathHelper.floor_double(par1);
746        int j = MathHelper.floor_double(par3);
747        int k = MathHelper.floor_double(par5);
748
749        if (BlockRailBase.isRailBlockAt(this.worldObj, i, j - 1, k))
750        {
751            --j;
752        }
753
754        int l = this.worldObj.getBlockId(i, j, k);
755
756        if (!BlockRailBase.isRailBlock(l))
757        {
758            return null;
759        }
760        else
761        {
762            int i1 = ((BlockRailBase)Block.blocksList[l]).getBasicRailMetadata(worldObj, this, i, j, k);
763
764            par3 = (double)j;
765
766            if (i1 >= 2 && i1 <= 5)
767            {
768                par3 = (double)(j + 1);
769            }
770
771            int[][] aint = matrix[i1];
772            double d4 = (double)(aint[1][0] - aint[0][0]);
773            double d5 = (double)(aint[1][2] - aint[0][2]);
774            double d6 = Math.sqrt(d4 * d4 + d5 * d5);
775            d4 /= d6;
776            d5 /= d6;
777            par1 += d4 * par7;
778            par5 += d5 * par7;
779
780            if (aint[0][1] != 0 && MathHelper.floor_double(par1) - i == aint[0][0] && MathHelper.floor_double(par5) - k == aint[0][2])
781            {
782                par3 += (double)aint[0][1];
783            }
784            else if (aint[1][1] != 0 && MathHelper.floor_double(par1) - i == aint[1][0] && MathHelper.floor_double(par5) - k == aint[1][2])
785            {
786                par3 += (double)aint[1][1];
787            }
788
789            return this.func_70489_a(par1, par3, par5);
790        }
791    }
792
793    public Vec3 func_70489_a(double par1, double par3, double par5)
794    {
795        int i = MathHelper.floor_double(par1);
796        int j = MathHelper.floor_double(par3);
797        int k = MathHelper.floor_double(par5);
798
799        if (BlockRailBase.isRailBlockAt(this.worldObj, i, j - 1, k))
800        {
801            --j;
802        }
803
804        int l = this.worldObj.getBlockId(i, j, k);
805
806        if (BlockRailBase.isRailBlock(l))
807        {
808            int i1 = ((BlockRailBase)Block.blocksList[l]).getBasicRailMetadata(worldObj, this, i, j, k);
809            par3 = (double)j;
810
811            if (i1 >= 2 && i1 <= 5)
812            {
813                par3 = (double)(j + 1);
814            }
815
816            int[][] aint = matrix[i1];
817            double d3 = 0.0D;
818            double d4 = (double)i + 0.5D + (double)aint[0][0] * 0.5D;
819            double d5 = (double)j + 0.5D + (double)aint[0][1] * 0.5D;
820            double d6 = (double)k + 0.5D + (double)aint[0][2] * 0.5D;
821            double d7 = (double)i + 0.5D + (double)aint[1][0] * 0.5D;
822            double d8 = (double)j + 0.5D + (double)aint[1][1] * 0.5D;
823            double d9 = (double)k + 0.5D + (double)aint[1][2] * 0.5D;
824            double d10 = d7 - d4;
825            double d11 = (d8 - d5) * 2.0D;
826            double d12 = d9 - d6;
827
828            if (d10 == 0.0D)
829            {
830                par1 = (double)i + 0.5D;
831                d3 = par5 - (double)k;
832            }
833            else if (d12 == 0.0D)
834            {
835                par5 = (double)k + 0.5D;
836                d3 = par1 - (double)i;
837            }
838            else
839            {
840                double d13 = par1 - d4;
841                double d14 = par5 - d6;
842                d3 = (d13 * d10 + d14 * d12) * 2.0D;
843            }
844
845            par1 = d4 + d10 * d3;
846            par3 = d5 + d11 * d3;
847            par5 = d6 + d12 * d3;
848
849            if (d11 < 0.0D)
850            {
851                ++par3;
852            }
853
854            if (d11 > 0.0D)
855            {
856                par3 += 0.5D;
857            }
858
859            return this.worldObj.getWorldVec3Pool().getVecFromPool(par1, par3, par5);
860        }
861        else
862        {
863            return null;
864        }
865    }
866
867    /**
868     * (abstract) Protected helper method to read subclass entity data from NBT.
869     */
870    protected void readEntityFromNBT(NBTTagCompound par1NBTTagCompound)
871    {
872        if (par1NBTTagCompound.getBoolean("CustomDisplayTile"))
873        {
874            this.setDisplayTile(par1NBTTagCompound.getInteger("DisplayTile"));
875            this.setDisplayTileData(par1NBTTagCompound.getInteger("DisplayData"));
876            this.setDisplayTileOffset(par1NBTTagCompound.getInteger("DisplayOffset"));
877        }
878
879        if (par1NBTTagCompound.hasKey("CustomName") && par1NBTTagCompound.getString("CustomName").length() > 0)
880        {
881            this.entityName = par1NBTTagCompound.getString("CustomName");
882        }
883    }
884
885    /**
886     * (abstract) Protected helper method to write subclass entity data to NBT.
887     */
888    protected void writeEntityToNBT(NBTTagCompound par1NBTTagCompound)
889    {
890        if (this.hasDisplayTile())
891        {
892            par1NBTTagCompound.setBoolean("CustomDisplayTile", true);
893            par1NBTTagCompound.setInteger("DisplayTile", this.getDisplayTile() == null ? 0 : this.getDisplayTile().blockID);
894            par1NBTTagCompound.setInteger("DisplayData", this.getDisplayTileData());
895            par1NBTTagCompound.setInteger("DisplayOffset", this.getDisplayTileOffset());
896        }
897
898        if (this.entityName != null && this.entityName.length() > 0)
899        {
900            par1NBTTagCompound.setString("CustomName", this.entityName);
901        }
902    }
903
904    @SideOnly(Side.CLIENT)
905    public float getShadowSize()
906    {
907        return 0.0F;
908    }
909
910    /**
911     * Applies a velocity to each of the entities pushing them away from each other. Args: entity
912     */
913    public void applyEntityCollision(Entity par1Entity)
914    {
915        MinecraftForge.EVENT_BUS.post(new MinecartCollisionEvent(this, par1Entity));
916        if (getCollisionHandler() != null)
917        {
918            getCollisionHandler().onEntityCollision(this, par1Entity);
919            return;
920        }
921        if (!this.worldObj.isRemote)
922        {
923            if (par1Entity != this.riddenByEntity)
924            {
925                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)
926                {
927                    par1Entity.mountEntity(this);
928                }
929
930                double d0 = par1Entity.posX - this.posX;
931                double d1 = par1Entity.posZ - this.posZ;
932                double d2 = d0 * d0 + d1 * d1;
933
934                if (d2 >= 9.999999747378752E-5D)
935                {
936                    d2 = (double)MathHelper.sqrt_double(d2);
937                    d0 /= d2;
938                    d1 /= d2;
939                    double d3 = 1.0D / d2;
940
941                    if (d3 > 1.0D)
942                    {
943                        d3 = 1.0D;
944                    }
945
946                    d0 *= d3;
947                    d1 *= d3;
948                    d0 *= 0.10000000149011612D;
949                    d1 *= 0.10000000149011612D;
950                    d0 *= (double)(1.0F - this.entityCollisionReduction);
951                    d1 *= (double)(1.0F - this.entityCollisionReduction);
952                    d0 *= 0.5D;
953                    d1 *= 0.5D;
954
955                    if (par1Entity instanceof EntityMinecart)
956                    {
957                        double d4 = par1Entity.posX - this.posX;
958                        double d5 = par1Entity.posZ - this.posZ;
959                        Vec3 vec3 = this.worldObj.getWorldVec3Pool().getVecFromPool(d4, 0.0D, d5).normalize();
960                        Vec3 vec31 = this.worldObj.getWorldVec3Pool().getVecFromPool((double)MathHelper.cos(this.rotationYaw * (float)Math.PI / 180.0F), 0.0D, (double)MathHelper.sin(this.rotationYaw * (float)Math.PI / 180.0F)).normalize();
961                        double d6 = Math.abs(vec3.dotProduct(vec31));
962
963                        if (d6 < 0.800000011920929D)
964                        {
965                            return;
966                        }
967
968                        double d7 = par1Entity.motionX + this.motionX;
969                        double d8 = par1Entity.motionZ + this.motionZ;
970
971                        if (((EntityMinecart)par1Entity).isPoweredCart() && !isPoweredCart())
972                        {
973                            this.motionX *= 0.20000000298023224D;
974                            this.motionZ *= 0.20000000298023224D;
975                            this.addVelocity(par1Entity.motionX - d0, 0.0D, par1Entity.motionZ - d1);
976                            par1Entity.motionX *= 0.949999988079071D;
977                            par1Entity.motionZ *= 0.949999988079071D;
978                        }
979                        else if (!((EntityMinecart)par1Entity).isPoweredCart() && isPoweredCart())
980                        {
981                            par1Entity.motionX *= 0.20000000298023224D;
982                            par1Entity.motionZ *= 0.20000000298023224D;
983                            par1Entity.addVelocity(this.motionX + d0, 0.0D, this.motionZ + d1);
984                            this.motionX *= 0.949999988079071D;
985                            this.motionZ *= 0.949999988079071D;
986                        }
987                        else
988                        {
989                            d7 /= 2.0D;
990                            d8 /= 2.0D;
991                            this.motionX *= 0.20000000298023224D;
992                            this.motionZ *= 0.20000000298023224D;
993                            this.addVelocity(d7 - d0, 0.0D, d8 - d1);
994                            par1Entity.motionX *= 0.20000000298023224D;
995                            par1Entity.motionZ *= 0.20000000298023224D;
996                            par1Entity.addVelocity(d7 + d0, 0.0D, d8 + d1);
997                        }
998                    }
999                    else
1000                    {
1001                        this.addVelocity(-d0, 0.0D, -d1);
1002                        par1Entity.addVelocity(d0 / 4.0D, 0.0D, d1 / 4.0D);
1003                    }
1004                }
1005            }
1006        }
1007    }
1008
1009    @SideOnly(Side.CLIENT)
1010
1011    /**
1012     * Sets the position and rotation. Only difference from the other one is no bounding on the rotation. Args: posX,
1013     * posY, posZ, yaw, pitch
1014     */
1015    public void setPositionAndRotation2(double par1, double par3, double par5, float par7, float par8, int par9)
1016    {
1017        this.minecartX = par1;
1018        this.minecartY = par3;
1019        this.minecartZ = par5;
1020        this.minecartYaw = (double)par7;
1021        this.minecartPitch = (double)par8;
1022        this.turnProgress = par9 + 2;
1023        this.motionX = this.velocityX;
1024        this.motionY = this.velocityY;
1025        this.motionZ = this.velocityZ;
1026    }
1027
1028    @SideOnly(Side.CLIENT)
1029
1030    /**
1031     * Sets the velocity to the args. Args: x, y, z
1032     */
1033    public void setVelocity(double par1, double par3, double par5)
1034    {
1035        this.velocityX = this.motionX = par1;
1036        this.velocityY = this.motionY = par3;
1037        this.velocityZ = this.motionZ = par5;
1038    }
1039
1040    /**
1041     * Sets the current amount of damage the minecart has taken. Decreases over time. The cart breaks when this is over
1042     * 40.
1043     */
1044    public void setDamage(int par1)
1045    {
1046        this.dataWatcher.updateObject(19, Integer.valueOf(par1));
1047    }
1048
1049    /**
1050     * Gets the current amount of damage the minecart has taken. Decreases over time. The cart breaks when this is over
1051     * 40.
1052     */
1053    public int getDamage()
1054    {
1055        return this.dataWatcher.getWatchableObjectInt(19);
1056    }
1057
1058    /**
1059     * Sets the rolling amplitude the cart rolls while being attacked.
1060     */
1061    public void setRollingAmplitude(int par1)
1062    {
1063        this.dataWatcher.updateObject(17, Integer.valueOf(par1));
1064    }
1065
1066    /**
1067     * Gets the rolling amplitude the cart rolls while being attacked.
1068     */
1069    public int getRollingAmplitude()
1070    {
1071        return this.dataWatcher.getWatchableObjectInt(17);
1072    }
1073
1074    /**
1075     * Sets the rolling direction the cart rolls while being attacked. Can be 1 or -1.
1076     */
1077    public void setRollingDirection(int par1)
1078    {
1079        this.dataWatcher.updateObject(18, Integer.valueOf(par1));
1080    }
1081
1082    /**
1083     * Gets the rolling direction the cart rolls while being attacked. Can be 1 or -1.
1084     */
1085    public int getRollingDirection()
1086    {
1087        return this.dataWatcher.getWatchableObjectInt(18);
1088    }
1089
1090    public abstract int getMinecartType();
1091
1092    public Block getDisplayTile()
1093    {
1094        if (!this.hasDisplayTile())
1095        {
1096            return this.getDefaultDisplayTile();
1097        }
1098        else
1099        {
1100            int i = this.getDataWatcher().getWatchableObjectInt(20) & 65535;
1101            return i > 0 && i < Block.blocksList.length ? Block.blocksList[i] : null;
1102        }
1103    }
1104
1105    public Block getDefaultDisplayTile()
1106    {
1107        return null;
1108    }
1109
1110    public int getDisplayTileData()
1111    {
1112        return !this.hasDisplayTile() ? this.getDefaultDisplayTileData() : this.getDataWatcher().getWatchableObjectInt(20) >> 16;
1113    }
1114
1115    public int getDefaultDisplayTileData()
1116    {
1117        return 0;
1118    }
1119
1120    public int getDisplayTileOffset()
1121    {
1122        return !this.hasDisplayTile() ? this.getDefaultDisplayTileOffset() : this.getDataWatcher().getWatchableObjectInt(21);
1123    }
1124
1125    public int getDefaultDisplayTileOffset()
1126    {
1127        return 6;
1128    }
1129
1130    public void setDisplayTile(int par1)
1131    {
1132        this.getDataWatcher().updateObject(20, Integer.valueOf(par1 & 65535 | this.getDisplayTileData() << 16));
1133        this.setHasDisplayTile(true);
1134    }
1135
1136    public void setDisplayTileData(int par1)
1137    {
1138        Block block = this.getDisplayTile();
1139        int j = block == null ? 0 : block.blockID;
1140        this.getDataWatcher().updateObject(20, Integer.valueOf(j & 65535 | par1 << 16));
1141        this.setHasDisplayTile(true);
1142    }
1143
1144    public void setDisplayTileOffset(int par1)
1145    {
1146        this.getDataWatcher().updateObject(21, Integer.valueOf(par1));
1147        this.setHasDisplayTile(true);
1148    }
1149
1150    public boolean hasDisplayTile()
1151    {
1152        return this.getDataWatcher().getWatchableObjectByte(22) == 1;
1153    }
1154
1155    public void setHasDisplayTile(boolean par1)
1156    {
1157        this.getDataWatcher().updateObject(22, Byte.valueOf((byte)(par1 ? 1 : 0)));
1158    }
1159
1160    public void func_96094_a(String par1Str)
1161    {
1162        this.entityName = par1Str;
1163    }
1164
1165    /**
1166     * Gets the username of the entity.
1167     */
1168    public String getEntityName()
1169    {
1170        return this.entityName != null ? this.entityName : super.getEntityName();
1171    }
1172
1173    /**
1174     * If this returns false, the inventory name will be used as an unlocalized name, and translated into the player's
1175     * language. Otherwise it will be used directly.
1176     */
1177    public boolean isInvNameLocalized()
1178    {
1179        return this.entityName != null;
1180    }
1181
1182    public String func_95999_t()
1183    {
1184        return this.entityName;
1185    }
1186
1187    /**
1188     * Moved to allow overrides.
1189     * This code handles minecart movement and speed capping when on a rail.
1190     */
1191    public void moveMinecartOnRail(int x, int y, int z, double par4){
1192        double d12 = this.motionX;
1193        double d13 = this.motionZ;
1194
1195        if (this.riddenByEntity != null)
1196        {
1197            d12 *= 0.75D;
1198            d13 *= 0.75D;
1199        }
1200
1201        if (d12 < -par4)
1202        {
1203            d12 = -par4;
1204        }
1205
1206        if (d12 > par4)
1207        {
1208            d12 = par4;
1209        }
1210
1211        if (d13 < -par4)
1212        {
1213            d13 = -par4;
1214        }
1215
1216        if (d13 > par4)
1217        {
1218            d13 = par4;
1219        }
1220
1221        this.moveEntity(d12, 0.0D, d13);
1222    }
1223
1224    /**
1225     * Gets the current global Minecart Collision handler if none
1226     * is registered, returns null
1227     * @return The collision handler or null
1228     */
1229    public static IMinecartCollisionHandler getCollisionHandler()
1230    {
1231        return collisionHandler;
1232    }
1233
1234    /**
1235     * Sets the global Minecart Collision handler, overwrites any
1236     * that is currently set.
1237     * @param handler The new handler
1238     */
1239    public static void setCollisionHandler(IMinecartCollisionHandler handler)
1240    {
1241        collisionHandler = handler;
1242    }
1243
1244    /**
1245     * This function returns an ItemStack that represents this cart.
1246     * This should be an ItemStack that can be used by the player to place the cart,
1247     * but is not necessary the item the cart drops when destroyed.
1248     * @return An ItemStack that can be used to place the cart.
1249     */
1250    public ItemStack getCartItem()
1251    {
1252        if (this instanceof EntityMinecartChest)
1253        {
1254            return new ItemStack(Item.minecartCrate);
1255        }
1256        else if (this instanceof EntityMinecartTNT)
1257        {
1258            return new ItemStack(Item.tntMinecart);
1259        }
1260        else if (this instanceof EntityMinecartFurnace)
1261        {
1262            return new ItemStack(Item.minecartPowered);
1263        }
1264        else if (this instanceof EntityMinecartHopper)
1265        {
1266            return new ItemStack(Item.hopperMinecart);
1267        }
1268        return new ItemStack(Item.minecartEmpty);
1269    }
1270
1271    /**
1272     * Returns true if this cart can currently use rails.
1273     * This function is mainly used to gracefully detach a minecart from a rail.
1274     * @return True if the minecart can use rails.
1275     */
1276    public boolean canUseRail()
1277    {
1278        return canUseRail;
1279    }
1280
1281    /**
1282     * Set whether the minecart can use rails.
1283     * This function is mainly used to gracefully detach a minecart from a rail.
1284     * @param use Whether the minecart can currently use rails.
1285     */
1286    public void setCanUseRail(boolean use)
1287    {
1288        canUseRail = use;
1289    }
1290
1291    /**
1292     * Return false if this cart should not call onMinecartPass() and should ignore Powered Rails.
1293     * @return True if this cart should call onMinecartPass().
1294     */
1295    public boolean shouldDoRailFunctions()
1296    {
1297        return true;
1298    }
1299
1300    /**
1301     * Returns true if this cart is self propelled.
1302     * @return True if powered.
1303     */
1304    public boolean isPoweredCart()
1305    {
1306        return getMinecartType()== 2;
1307    }
1308
1309    /**
1310     * Returns true if this cart can be ridden by an Entity.
1311     * @return True if this cart can be ridden.
1312     */
1313    public boolean canBeRidden()
1314    {
1315        if(this instanceof EntityMinecartEmpty)
1316        {
1317            return true;
1318        }
1319        return false;
1320    }
1321
1322    /**
1323     * Getters/setters for physics variables
1324     */
1325
1326    /**
1327     * Returns the carts max speed when traveling on rails. Carts going faster
1328     * than 1.1 cause issues with chunk loading. Carts cant traverse slopes or
1329     * corners at greater than 0.5 - 0.6. This value is compared with the rails
1330     * max speed and the carts current speed cap to determine the carts current
1331     * max speed. A normal rail's max speed is 0.4.
1332     *
1333     * @return Carts max speed.
1334     */
1335    public float getMaxCartSpeedOnRail()
1336    {
1337        return 1.2f;
1338    }
1339
1340    /**
1341     * Returns the current speed cap for the cart when traveling on rails. This
1342     * functions differs from getMaxCartSpeedOnRail() in that it controls
1343     * current movement and cannot be overridden. The value however can never be
1344     * higher than getMaxCartSpeedOnRail().
1345     *
1346     * @return
1347     */
1348    public final float getCurrentCartSpeedCapOnRail()
1349    {
1350        return currentSpeedRail;
1351    }
1352
1353    public final void setCurrentCartSpeedCapOnRail(float value)
1354    {
1355        value = Math.min(value, getMaxCartSpeedOnRail());
1356        currentSpeedRail = value;
1357    }
1358
1359    public float getMaxSpeedAirLateral()
1360    {
1361        return maxSpeedAirLateral;
1362    }
1363
1364    public void setMaxSpeedAirLateral(float value)
1365    {
1366        maxSpeedAirLateral = value;
1367    }
1368
1369    public float getMaxSpeedAirVertical()
1370    {
1371        return maxSpeedAirVertical;
1372    }
1373
1374    public void setMaxSpeedAirVertical(float value)
1375    {
1376        maxSpeedAirVertical = value;
1377    }
1378
1379    public double getDragAir()
1380    {
1381        return dragAir;
1382    }
1383
1384    public void setDragAir(double value)
1385    {
1386        dragAir = value;
1387    }
1388
1389    public double getSlopeAdjustment()
1390    {
1391        return 0.0078125D;
1392    }
1393}