package common.entity;

import java.util.List;

import common.block.Block;
import common.block.Material;
import common.block.SoundType;
import common.block.artificial.BlockFence;
import common.block.artificial.BlockFenceGate;
import common.block.artificial.BlockWall;
import common.block.liquid.BlockLiquid;
import common.color.TextColor;
import common.dimension.DimType;
import common.enchantment.Enchantment;
import common.enchantment.EnchantmentHelper;
import common.entity.effect.EntityLightning;
import common.entity.item.EntityItem;
import common.entity.npc.EntityNPC;
import common.entity.types.EntityLiving;
import common.init.BlockRegistry;
import common.init.Blocks;
import common.init.EntityRegistry;
import common.init.ItemRegistry;
import common.init.SoundEvent;
import common.init.UniverseRegistry;
import common.item.Item;
import common.item.ItemStack;
import common.model.ParticleType;
import common.rng.Random;
import common.tags.TagObject;
import common.util.BlockPos;
import common.util.BoundingBox;
import common.util.ExtMath;
import common.util.Facing;
import common.util.HitPosition;
import common.util.PortalType;
import common.util.Position;
import common.util.Vec3;
import common.vars.Vars;
import common.world.Explosion;
import common.world.State;
import common.world.World;
import common.world.AWorldServer;

public abstract class Entity
{
    private static final BoundingBox ZERO_AABB = new BoundingBox(0.0D, 0.0D, 0.0D, 0.0D, 0.0D, 0.0D);
    private static int nextID;
    
    protected final DataWatcher dataWatcher;
    protected final Random rand;
    
    private int eid;
    public World worldObj;
    private BoundingBox bbox;
    public Entity passenger;
    public Entity vehicle;
    
    public double renderDistWeight;
    public boolean preventSpawning;
    public boolean forceSpawn;
    public boolean ignoreFall;
    public double prevX;
    public double prevY;
    public double prevZ;
    public double posX;
    public double posY;
    public double posZ;
    public double motionX;
    public double motionY;
    public double motionZ;
    public float rotYaw;
    public float rotPitch;
    public float prevYaw;
    public float prevPitch;
    public boolean onGround;
    public boolean collidedHorizontally;
    public boolean collidedVertically;
    public boolean collided;
    public boolean veloChanged;
    protected boolean inWeb;
    private boolean outsideBorder;
    public boolean dead;
    public float width;
    public float height;
    public float prevWalkDistMod;
    public float walkDistMod;
    public float stepDistMod;
    public float fallDistance;
    private int nextStepDist;
    public double lastTickPosX;
    public double lastTickPosY;
    public double lastTickPosZ;
    public float stepHeight;
    public boolean noClip;
    public float collisionReduction;
    public int ticksExisted;
    public int fireResistance;
    private int fire;
    protected boolean inLiquid;
    public int hurtResistance;
    protected boolean firstUpdate;
//    protected boolean fireImmune;
    private double riderPitchDelta;
    private double riderYawDelta;
    public boolean addedToChunk;
    public int chunkCoordX;
    public int chunkCoordY;
    public int chunkCoordZ;
    public int serverPosX;
    public int serverPosY;
    public int serverPosZ;
    public boolean noFrustumCheck;
    public boolean isAirBorne;
    protected PortalType inPortal;
//    private boolean invulnerable;

    public int getId()
    {
        return this.eid;
    }

    public void setId(int id)
    {
        this.eid = id;
    }

    public Entity(World worldIn)
    {
        this.eid = nextID++;
//        if(!worldIn.client && Config.persistEntities) {
//        	do {
//        		this.persistentId = Server.getServer().getRNG().longv();
//        	}
//        	while(this.persistentId == 0L);
//        }
        this.renderDistWeight = 1.0D;
        this.bbox = ZERO_AABB;
        this.width = 0.6F;
        this.height = 1.8F;
        this.nextStepDist = 1;
        this.rand = new Random();
        this.fireResistance = 1;
        this.firstUpdate = true;
        this.worldObj = worldIn;
        this.setPosition(0.0D, 0.0D, 0.0D);

        this.dataWatcher = new DataWatcher(this);
        this.dataWatcher.addObject(0, Byte.valueOf((byte)0));
        this.dataWatcher.addObject(1, "");
        this.entityInit();
    }

    protected abstract void entityInit();

    public final DataWatcher getDataWatcher()
    {
        return this.dataWatcher;
    }

    public boolean equals(Object obj)
    {
        return obj instanceof Entity ? ((Entity)obj).eid == this.eid : false;
    }

    public int hashCode()
    {
        return this.eid;
    }

    /**
     * Keeps moving the entity up so it isn't colliding with blocks and other requirements for this entity to be spawned
     * (only actually used on players though its also on Entity)
     */
    protected void preparePlayerToSpawn()
    {
        if (this.worldObj != null)
        {
            while (this.posY > 0.0D && this.posY < 512.0D)
            {
                this.setPosition(this.posX, this.posY, this.posZ);

                if (this.worldObj.getCollidingBoundingBoxes(this, this.getEntityBoundingBox()).isEmpty())
                {
                    break;
                }

                ++this.posY;
            }

            this.motionX = this.motionY = this.motionZ = 0.0D;
            this.rotPitch = 0.0F;
        }
    }

    /**
     * Will get destroyed next tick.
     */
    public void setDead()
    {
        this.dead = true;
    }

    /**
     * Sets the width and height of the entity. Args: width, height
     */
    protected void setSize(float width, float height)
    {
        if (width != this.width || height != this.height)
        {
            float f = this.width;
            this.width = width;
            this.height = height;
            this.setEntityBoundingBox(new BoundingBox(this.getEntityBoundingBox().minX, this.getEntityBoundingBox().minY, this.getEntityBoundingBox().minZ, this.getEntityBoundingBox().minX + (double)this.width, this.getEntityBoundingBox().minY + (double)this.height, this.getEntityBoundingBox().minZ + (double)this.width));

            if (this.width > f && !this.firstUpdate && !this.worldObj.client)
            {
                this.moveEntity((double)(f - this.width), 0.0D, (double)(f - this.width));
            }
        }
    }

    /**
     * Sets the rotation of the entity. Args: yaw, pitch (both in degrees)
     */
    protected void setRotation(float yaw, float pitch)
    {
        this.rotYaw = yaw % 360.0F;
        this.rotPitch = pitch % 360.0F;
    }

    /**
     * Sets the x,y,z of the entity from the given parameters. Also seems to set up a bounding box.
     */
    public void setPosition(double x, double y, double z)
    {
        this.posX = x;
        this.posY = y;
        this.posZ = z;
        float f = this.width / 2.0F;
        float f1 = this.height;
        this.setEntityBoundingBox(new BoundingBox(x - (double)f, y, z - (double)f, x + (double)f, y + (double)f1, z + (double)f));
    }

    /**
     * Adds 15% to the entity's yaw and subtracts 15% from the pitch. Clamps pitch from -90 to 90. Both arguments in
     * degrees.
     */
    public void setAngles(float yaw, float pitch)
    {
        float f = this.rotPitch;
        float f1 = this.rotYaw;
        this.rotYaw = (float)((double)this.rotYaw + (double)yaw * 0.15D);
        this.rotPitch = (float)((double)this.rotPitch - (double)pitch * 0.15D);
        this.rotPitch = ExtMath.clampf(this.rotPitch, -90.0F, 90.0F);
        this.prevPitch += this.rotPitch - f;
        this.prevYaw += this.rotYaw - f1;
    }

    /**
     * Called to update the entity's position/logic.
     */
    public void onUpdate()
    {
        this.onEntityUpdate();
    }
    
	protected void checkPortal() {
		if(this.inPortal != null) {
			if(this.vehicle == null && this.passenger == null && Vars.portals) {
	    		int current = this.worldObj.dimension.getDimensionId();
				int dest = UniverseRegistry.getPortalDest(current, this.inPortal);
				if(dest != current) {
					this.travelToDimension(dest, null, 0.0f, 0.0f, this.inPortal);
					this.inPortal = null;
				}
			}
		}
	}

    /**
     * Gets called every tick from main Entity class
     */
    public void onEntityUpdate()
    {
//        this.worldObj.profiler.start("entityBaseTick");

        if (this.vehicle != null && this.vehicle.dead)
        {
            this.vehicle = null;
        }

        this.prevWalkDistMod = this.walkDistMod;
        this.prevX = this.posX;
        this.prevY = this.posY;
        this.prevZ = this.posZ;
        this.prevPitch = this.rotPitch;
        this.prevYaw = this.rotYaw;

        if (!this.worldObj.client)
        {
        	this.checkPortal();
        }

        this.spawnRunningParticles();
        this.handleWaterMovement();

        if (this.worldObj.client)
        {
            this.fire = 0;
        }
        else if (this.fire > 0)
        {
            if (this.isImmuneToFire())
            {
                this.fire -= 4;

                if (this.fire < 0)
                {
                    this.fire = 0;
                }
            }
            else
            {
                if (Vars.damageFire && this.fire % 20 == 0)
                {
                    this.attackEntityFrom(DamageSource.onFire, 1);
                }

                --this.fire;
            }
        }

        if (this.isInMolten())
        {
            this.setOnFireFromLava();
            this.fallDistance *= 0.5F;
        }
        else if(this.worldObj.canBurnAt(this.getPosition()) && this.isInDownfall()) {
        	this.setOnFireFromMolten();
        }

        if (this.posY < (double)(-World.MAX_SIZE_Y) - 64.0)
        {
            this.onVoidUpdate();
        }

        if (!this.worldObj.client)
        {
            this.setFlag(0, this.fire > 0);
        }

        this.firstUpdate = false;
//        this.worldObj.profiler.end();
    }
    
    protected void onVoidUpdate() {
    	if(!this.worldObj.client && this.worldObj.dimension.getType() != DimType.SPACE) {
//            this.worldObj.profiler.start("descent");
    		int current = this.worldObj.dimension.getDimensionId();
			int dim = Vars.voidPortal ? UniverseRegistry.getPortalDest(current, PortalType.VOID) : current;
			if(dim == current) { //  && (!(this.isPlayer()) || !((EntityNPCMP)this).creative)) {
				this.kill();
			}
//			else if(dim == current) {
//				this.setY((float)ExtMath.clampi(Config.portalHeight, 0, 16384));
//				this.ignoreFall = true;
//			}
			else if(this.vehicle == null && this.passenger == null) {
				Entity ent = this.travelToDimension(dim, 
						new BlockPos(this.posX, (float)ExtMath.clampi(Vars.portalHeight, 0, 16384), this.posZ), 
						this.rotYaw, this.rotPitch, PortalType.VOID);
				ent.ignoreFall = true;
			}
//            this.worldObj.profiler.end();
    	}
//    	else if(this.worldObj.dimension.getGravity() != 0.0f) {
//    		this.kill();
//    	}
    }

//    /**
//     * Return the amount of time this entity should stay in a portal before being transported.
//     */
//    public int getMaxInPortalTime()
//    {
//        return 0;
//    }

    /**
     * Called whenever the entity is walking inside of lava.
     */
    protected void setOnFireFromLava()
    {
        if (!this.isImmuneToFire())
        {
        	if(Vars.damageLava)
        		this.attackEntityFrom(DamageSource.hotLiquid, 4);
            this.setFire(15);
        }
    }
    
    protected void setOnFireFromMolten()
    {
        if (!this.isImmuneToFire())
        {
        	if(Vars.damageMolten)
        		this.attackEntityFrom(DamageSource.molten, 2);
            this.setFire(10);
        }
    }
    
    public void setOnFireFromObject(int ticks)
    {
        if(!this.isImmuneToFire())
            this.setFire(ticks);
    }

    /**
     * Sets entity to burn for x amount of seconds, cannot lower amount of existing fire.
     */
    public void setFire(int seconds)
    {
        int i = seconds * 20;
        i = Enchantment.getFireTimeForEntity(this, i);

        if (this.fire < i)
        {
            this.fire = i;
        }
    }
    
    public void setFire()
    {
        this.fire = Integer.MAX_VALUE / 2;
    }

    /**
     * Removes fire from entity.
     */
    public void extinguish()
    {
        this.fire = 0;
    }
    
    public void kill()
    {
        this.setDead();
    }

    /**
     * Checks if the offset position from the entity's current position is inside of liquid. Args: x, y, z
     */
    public boolean isOffsetPositionInLiquid(double x, double y, double z)
    {
        BoundingBox axisalignedbb = this.getEntityBoundingBox().offset(x, y, z);
        return this.isLiquidPresentInAABB(axisalignedbb);
    }

    /**
     * Determines if a liquid is present within the specified AxisAlignedBB.
     */
    private boolean isLiquidPresentInAABB(BoundingBox bb)
    {
        return this.worldObj.getCollidingBoundingBoxes(this, bb).isEmpty() && !this.worldObj.isAnyLiquid(bb);
    }

    /**
     * Tries to moves the entity by the passed in displacement. Args: x, y, z
     */
    public void moveEntity(double x, double y, double z)
    {
        if (this.noClip)
        {
            this.setEntityBoundingBox(this.getEntityBoundingBox().offset(x, y, z));
            this.resetPositionToBB();
        }
        else
        {
//            this.worldObj.profiler.start("move");
            double d0 = this.posX;
            double d1 = this.posY;
            double d2 = this.posZ;

            if (this.inWeb)
            {
                this.inWeb = false;
                x *= 0.25D;
                y *= 0.05000000074505806D;
                z *= 0.25D;
                this.motionX = 0.0D;
                this.motionY = 0.0D;
                this.motionZ = 0.0D;
            }

            double d3 = x;
            double d4 = y;
            double d5 = z;
            boolean flag = this.onGround && this.isSneaking() && this.isPlayer();

            if (flag)
            {
                double d6;

                for (d6 = 0.05D; x != 0.0D && this.worldObj.getCollidingBoundingBoxes(this, this.getEntityBoundingBox().offset(x, -1.0D, 0.0D)).isEmpty(); d3 = x)
                {
                    if (x < d6 && x >= -d6)
                    {
                        x = 0.0D;
                    }
                    else if (x > 0.0D)
                    {
                        x -= d6;
                    }
                    else
                    {
                        x += d6;
                    }
                }

                for (; z != 0.0D && this.worldObj.getCollidingBoundingBoxes(this, this.getEntityBoundingBox().offset(0.0D, -1.0D, z)).isEmpty(); d5 = z)
                {
                    if (z < d6 && z >= -d6)
                    {
                        z = 0.0D;
                    }
                    else if (z > 0.0D)
                    {
                        z -= d6;
                    }
                    else
                    {
                        z += d6;
                    }
                }

                for (; x != 0.0D && z != 0.0D && this.worldObj.getCollidingBoundingBoxes(this, this.getEntityBoundingBox().offset(x, -1.0D, z)).isEmpty(); d5 = z)
                {
                    if (x < d6 && x >= -d6)
                    {
                        x = 0.0D;
                    }
                    else if (x > 0.0D)
                    {
                        x -= d6;
                    }
                    else
                    {
                        x += d6;
                    }

                    d3 = x;

                    if (z < d6 && z >= -d6)
                    {
                        z = 0.0D;
                    }
                    else if (z > 0.0D)
                    {
                        z -= d6;
                    }
                    else
                    {
                        z += d6;
                    }
                }
            }

            List<BoundingBox> list1 = this.worldObj.getCollidingBoundingBoxes(this, this.getEntityBoundingBox().addCoord(x, y, z));
            BoundingBox axisalignedbb = this.getEntityBoundingBox();

            for (BoundingBox axisalignedbb1 : list1)
            {
                y = axisalignedbb1.calculateYOffset(this.getEntityBoundingBox(), y);
            }

            this.setEntityBoundingBox(this.getEntityBoundingBox().offset(0.0D, y, 0.0D));
            boolean flag1 = this.onGround || d4 != y && d4 < 0.0D;

            for (BoundingBox axisalignedbb2 : list1)
            {
                x = axisalignedbb2.calculateXOffset(this.getEntityBoundingBox(), x);
            }

            this.setEntityBoundingBox(this.getEntityBoundingBox().offset(x, 0.0D, 0.0D));

            for (BoundingBox axisalignedbb13 : list1)
            {
                z = axisalignedbb13.calculateZOffset(this.getEntityBoundingBox(), z);
            }

            this.setEntityBoundingBox(this.getEntityBoundingBox().offset(0.0D, 0.0D, z));

            if (this.stepHeight > 0.0F && flag1 && (d3 != x || d5 != z))
            {
                double d11 = x;
                double d7 = y;
                double d8 = z;
                BoundingBox axisalignedbb3 = this.getEntityBoundingBox();
                this.setEntityBoundingBox(axisalignedbb);
                y = (double)this.stepHeight;
                List<BoundingBox> list = this.worldObj.getCollidingBoundingBoxes(this, this.getEntityBoundingBox().addCoord(d3, y, d5));
                BoundingBox axisalignedbb4 = this.getEntityBoundingBox();
                BoundingBox axisalignedbb5 = axisalignedbb4.addCoord(d3, 0.0D, d5);
                double d9 = y;

                for (BoundingBox axisalignedbb6 : list)
                {
                    d9 = axisalignedbb6.calculateYOffset(axisalignedbb5, d9);
                }

                axisalignedbb4 = axisalignedbb4.offset(0.0D, d9, 0.0D);
                double d15 = d3;

                for (BoundingBox axisalignedbb7 : list)
                {
                    d15 = axisalignedbb7.calculateXOffset(axisalignedbb4, d15);
                }

                axisalignedbb4 = axisalignedbb4.offset(d15, 0.0D, 0.0D);
                double d16 = d5;

                for (BoundingBox axisalignedbb8 : list)
                {
                    d16 = axisalignedbb8.calculateZOffset(axisalignedbb4, d16);
                }

                axisalignedbb4 = axisalignedbb4.offset(0.0D, 0.0D, d16);
                BoundingBox axisalignedbb14 = this.getEntityBoundingBox();
                double d17 = y;

                for (BoundingBox axisalignedbb9 : list)
                {
                    d17 = axisalignedbb9.calculateYOffset(axisalignedbb14, d17);
                }

                axisalignedbb14 = axisalignedbb14.offset(0.0D, d17, 0.0D);
                double d18 = d3;

                for (BoundingBox axisalignedbb10 : list)
                {
                    d18 = axisalignedbb10.calculateXOffset(axisalignedbb14, d18);
                }

                axisalignedbb14 = axisalignedbb14.offset(d18, 0.0D, 0.0D);
                double d19 = d5;

                for (BoundingBox axisalignedbb11 : list)
                {
                    d19 = axisalignedbb11.calculateZOffset(axisalignedbb14, d19);
                }

                axisalignedbb14 = axisalignedbb14.offset(0.0D, 0.0D, d19);
                double d20 = d15 * d15 + d16 * d16;
                double d10 = d18 * d18 + d19 * d19;

                if (d20 > d10)
                {
                    x = d15;
                    z = d16;
                    y = -d9;
                    this.setEntityBoundingBox(axisalignedbb4);
                }
                else
                {
                    x = d18;
                    z = d19;
                    y = -d17;
                    this.setEntityBoundingBox(axisalignedbb14);
                }

                for (BoundingBox axisalignedbb12 : list)
                {
                    y = axisalignedbb12.calculateYOffset(this.getEntityBoundingBox(), y);
                }

                this.setEntityBoundingBox(this.getEntityBoundingBox().offset(0.0D, y, 0.0D));

                if (d11 * d11 + d8 * d8 >= x * x + z * z)
                {
                    x = d11;
                    y = d7;
                    z = d8;
                    this.setEntityBoundingBox(axisalignedbb3);
                }
            }

//            this.worldObj.profiler.end();
//            this.worldObj.profiler.start("rest");
            this.resetPositionToBB();
            this.collidedHorizontally = d3 != x || d5 != z;
            this.collidedVertically = d4 != y;
            this.onGround = this.collidedVertically && d4 < 0.0D;
            this.collided = this.collidedHorizontally || this.collidedVertically;
            int i = ExtMath.floord(this.posX);
            int j = ExtMath.floord(this.posY - 0.20000000298023224D);
            int k = ExtMath.floord(this.posZ);
            BlockPos blockpos = new BlockPos(i, j, k);
            Block block1 = this.worldObj.getState(blockpos).getBlock();

            if (block1 == Blocks.air)
            {
                Block block = this.worldObj.getState(blockpos.down()).getBlock();

                if (block instanceof BlockFence || block instanceof BlockWall || block instanceof BlockFenceGate)
                {
                    block1 = block;
                    blockpos = blockpos.down();
                }
            }

            this.updateFallState(y, this.onGround, block1, blockpos);

            if (d3 != x)
            {
                this.motionX = 0.0D;
            }

            if (d5 != z)
            {
                this.motionZ = 0.0D;
            }

            if (d4 != y)
            {
                block1.onLanded(this.worldObj, this);
            }

            if (this.canTriggerWalking() && !flag && this.vehicle == null)
            {
                double d12 = this.posX - d0;
                double d13 = this.posY - d1;
                double d14 = this.posZ - d2;

                if (block1 != Blocks.ladder)
                {
                    d13 = 0.0D;
                }

                if (block1 != null && this.onGround)
                {
                    block1.onEntityCollidedWithBlock(this.worldObj, blockpos, this);
                }

                this.walkDistMod = (float)((double)this.walkDistMod + (double)ExtMath.sqrtd(d12 * d12 + d14 * d14) * 0.6D);
                this.stepDistMod = (float)((double)this.stepDistMod + (double)ExtMath.sqrtd(d12 * d12 + d13 * d13 + d14 * d14) * 0.6D);

                if (this.stepDistMod > (float)this.nextStepDist && block1 != Blocks.air)
                {
                    this.nextStepDist = (int)this.stepDistMod + 1;

                    if (this.isInLiquid())
                    {
                        float f = ExtMath.sqrtd(this.motionX * this.motionX * 0.20000000298023224D + this.motionY * this.motionY + this.motionZ * this.motionZ * 0.20000000298023224D) * 0.35F;

                        if (f > 1.0F)
                        {
                            f = 1.0F;
                        }

//                        this.playSound(this.getSwimSound(), f, 1.0F + (this.rand.floatv() - this.rand.floatv()) * 0.4F);
                    }

                    this.playStepSound(blockpos, block1);
                }
            }

            this.doBlockCollisions();

            boolean flag2 = this.isWet();

            if (this.worldObj.isFlammableWithin(this.getEntityBoundingBox().contract(0.001D, 0.001D, 0.001D)))
            {
                this.dealFireDamage(1);

                if (!flag2)
                {
                    ++this.fire;

                    if (this.fire == 0)
                    {
                        this.setFire(8);
                    }
                }
            }
            else if (this.fire <= 0)
            {
                this.fire = -this.fireResistance;
            }

            if (flag2 && this.fire > 0)
            {
                this.playSound(SoundEvent.FIZZ, 0.7F);
                this.fire = -this.fireResistance;
            }

//            this.worldObj.profiler.end();
        }
    }

    /**
     * Resets the entity's position to the center (planar) and bottom (vertical) points of its bounding box.
     */
    private void resetPositionToBB()
    {
        this.posX = (this.getEntityBoundingBox().minX + this.getEntityBoundingBox().maxX) / 2.0D;
        this.posY = this.getEntityBoundingBox().minY;
        this.posZ = (this.getEntityBoundingBox().minZ + this.getEntityBoundingBox().maxZ) / 2.0D;
    }

//    protected String getSwimSound()
//    {
//        return "random.swim";
//    }

    protected void doBlockCollisions()
    {
        BlockPos blockpos = new BlockPos(this.getEntityBoundingBox().minX + 0.001D, this.getEntityBoundingBox().minY + 0.001D, this.getEntityBoundingBox().minZ + 0.001D);
        BlockPos blockpos1 = new BlockPos(this.getEntityBoundingBox().maxX - 0.001D, this.getEntityBoundingBox().maxY - 0.001D, this.getEntityBoundingBox().maxZ - 0.001D);

        if (this.worldObj.isAreaLoaded(blockpos, blockpos1))
        {
            for (int i = blockpos.getX(); i <= blockpos1.getX(); ++i)
            {
                for (int j = blockpos.getY(); j <= blockpos1.getY(); ++j)
                {
                    for (int k = blockpos.getZ(); k <= blockpos1.getZ(); ++k)
                    {
                        BlockPos blockpos2 = new BlockPos(i, j, k);
                        State iblockstate = this.worldObj.getState(blockpos2);

                        iblockstate.getBlock().onEntityCollidedWithBlock(this.worldObj, blockpos2, iblockstate, this);
                    }
                }
            }
        }
    }

    protected void playStepSound(BlockPos pos, Block blockIn)
    {
        SoundType block$soundtype = blockIn.sound;

        if (this.worldObj.getState(pos.up()).getBlock() == Blocks.snow_layer)
        {
            block$soundtype = Blocks.snow_layer.sound;
            if(block$soundtype.getStepSound() != null)
            	this.playSound(block$soundtype.getStepSound(), 0.15F);
        }
        else if (!blockIn.getMaterial().isLiquid() && block$soundtype.getStepSound() != null)
        {
            this.playSound(block$soundtype.getStepSound(), 0.15F);
        }
    }

    public void playSound(SoundEvent name, float volume)
    {
//        if (!this.isSilent())
//        {
        this.worldObj.playSoundAtEntity(this, name, volume);
//        }
    }

//    /**
//     * @return True if this entity will not play sounds
//     */
//    public boolean isSilent()
//    {
//        return this.dataWatcher.getWatchableObjectByte(4) == 1;
//    }

//    /**
//     * When set to true the entity will not play sounds.
//     */
//    public void setSilent(boolean isSilent)
//    {
//        this.dataWatcher.updateObject(4, Byte.valueOf((byte)(isSilent ? 1 : 0)));
//    }

    /**
     * returns if this entity triggers Block.onEntityWalking on the blocks they walk on. used for spiders and wolves to
     * prevent them from trampling crops
     */
    protected boolean canTriggerWalking()
    {
        return true;
    }

    protected void updateFallState(double y, boolean onGroundIn, Block blockIn, BlockPos pos)
    {
        if (onGroundIn)
        {
            if (this.fallDistance > 0.0F)
            {
            	if(this.ignoreFall) {
            		this.ignoreFall = false;
            	}
            	else if (blockIn != null)
                {
                    blockIn.onFallenUpon(this.worldObj, pos, this, this.fallDistance);
                }
                else
                {
                    this.fall(this.fallDistance, 1.0F);
                }

                this.fallDistance = 0.0F;
            }
        }
        else if (y < 0.0D)
        {
            this.fallDistance = (float)((double)this.fallDistance - y);
        }
    }

    /**
     * Returns the collision bounding box for this entity
     */
    public BoundingBox getCollisionBoundingBox()
    {
        return null;
    }

    /**
     * Will deal the specified amount of damage to the entity if the entity isn't immune to fire damage. Args:
     * amountDamage
     */
    protected void dealFireDamage(int amount)
    {
        if(!this.isImmuneToFire() && Vars.damageFire)
            this.attackEntityFrom(DamageSource.inFire, amount);
    }

    public boolean isImmuneToFire()
    {
        return false;
    }

    public void fall(float distance, float damageMultiplier)
    {
        if (this.passenger != null)
        {
            this.passenger.fall(distance, damageMultiplier);
        }
    }

    /**
     * Checks if this entity is either in water or on an open air block in rain (used in wolves).
     */
    public boolean isWet()
    {
        return this.inLiquid || this.worldObj.isRainingAt(new BlockPos(this.posX, this.posY, this.posZ), true) || this.worldObj.isRainingAt(new BlockPos(this.posX, this.posY + (double)this.height, this.posZ), true);
    }
    
    public boolean isInDownfall()
    {
        return this.worldObj.isRainingAt(new BlockPos(this.posX, this.posY, this.posZ), false) || this.worldObj.isRainingAt(new BlockPos(this.posX, this.posY + (double)this.height, this.posZ), false);
    }

    /**
     * Checks if this entity is inside water (if inWater field is true as a result of handleWaterMovement() returning
     * true)
     */
    public boolean isInLiquid()
    {
        return this.inLiquid;
    }

    /**
     * Returns if this entity is in water and will end up adding the waters velocity to the entity
     */
    public boolean handleWaterMovement()
    {
        if (this.worldObj.handleLiquidAcceleration(this.getEntityBoundingBox().expand(0.0D, -0.4000000059604645D, 0.0D).contract(0.001D, 0.001D, 0.001D), this))
        {
            if (!this.inLiquid && !this.firstUpdate)
            {
                this.onEnterLiquid();
            }

            this.fallDistance = 0.0F;
            this.inLiquid = true;
            this.fire = 0;
        }
        else
        {
            this.inLiquid = false;
        }

        return this.inLiquid;
    }
    
    protected void onEnterLiquid()
    {
        float f = ExtMath.sqrtd(this.motionX * this.motionX * 0.20000000298023224D + this.motionY * this.motionY + this.motionZ * this.motionZ * 0.20000000298023224D) * 0.2F;

        if (f > 1.0F)
        {
            f = 1.0F;
        }

        this.playSound(SoundEvent.SPLASH, f);
        float f1 = (float)ExtMath.floord(this.getEntityBoundingBox().minY);

        for (int i = 0; (float)i < 1.0F + this.width * 20.0F; ++i)
        {
            float f2 = (this.rand.floatv() * 2.0F - 1.0F) * this.width;
            float f3 = (this.rand.floatv() * 2.0F - 1.0F) * this.width;
            this.worldObj.spawnParticle(ParticleType.WATER_BUBBLE, this.posX + (double)f2, (double)(f1 + 1.0F), this.posZ + (double)f3, this.motionX, this.motionY - (double)(this.rand.floatv() * 0.2F), this.motionZ);
        }

        if(this.worldObj.getState(new BlockPos(this.posX, this.posY, this.posZ)).getBlock().getMaterial() == Material.WATER) {
	        for (int j = 0; (float)j < 1.0F + this.width * 20.0F; ++j)
	        {
	            float f4 = (this.rand.floatv() * 2.0F - 1.0F) * this.width;
	            float f5 = (this.rand.floatv() * 2.0F - 1.0F) * this.width;
	            this.worldObj.spawnParticle(ParticleType.WATER_SPLASH, this.posX + (double)f4, (double)(f1 + 1.0F), this.posZ + (double)f5, this.motionX, this.motionY, this.motionZ);
	        }
        }
    }

    /**
     * Attempts to create sprinting particles if the entity is sprinting and not in water.
     */
    public void spawnRunningParticles()
    {
        if (this.isSprinting() && !this.isInLiquid())
        {
            this.createRunningParticles();
        }
    }

    protected void createRunningParticles()
    {
        int i = ExtMath.floord(this.posX);
        int j = ExtMath.floord(this.posY - 0.20000000298023224D);
        int k = ExtMath.floord(this.posZ);
        BlockPos blockpos = new BlockPos(i, j, k);
        State iblockstate = this.worldObj.getState(blockpos);
        Block block = iblockstate.getBlock();

        if (block.getRenderType() != -1)
        {
            this.worldObj.spawnParticle(ParticleType.BLOCK_CRACK, this.posX + ((double)this.rand.floatv() - 0.5D) * (double)this.width, this.getEntityBoundingBox().minY + 0.1D, this.posZ + ((double)this.rand.floatv() - 0.5D) * (double)this.width, -this.motionX * 4.0D, 1.5D, -this.motionZ * 4.0D, BlockRegistry.getStateId(iblockstate));
        }
    }

//    protected String getSplashSound()
//    {
//        return "random.splash";
//    }

    /**
     * Checks if the current block the entity is within of the specified material type
     */
    public boolean isInsideOfLiquid()
    {
        double d0 = this.posY + (double)this.getEyeHeight();
        BlockPos blockpos = new BlockPos(this.posX, d0, this.posZ);
        State iblockstate = this.worldObj.getState(blockpos);
        Block block = iblockstate.getBlock();

        if (block.getMaterial().isColdLiquid())
        {
            float f = BlockLiquid.getLiquidHeightPercent(iblockstate.getValue(BlockLiquid.LEVEL)) - 0.11111111F;
            float f1 = (float)(blockpos.getY() + 1) - f;
            boolean flag = d0 < (double)f1;
            return !flag && this.isPlayer() ? false : flag;
        }
        else
        {
            return false;
        }
    }

    public boolean isInMolten()
    {
        return this.worldObj.isMaterialInMolten(this.getEntityBoundingBox().expand(-0.10000000149011612D, -0.4000000059604645D, -0.10000000149011612D));
    }

    /**
     * Used in both water and by flying objects
     */
    public void moveFlying(float strafe, float forward, float friction)
    {
        float f = strafe * strafe + forward * forward;

        if (f >= 1.0E-4F)
        {
            f = ExtMath.sqrtf(f);

            if (f < 1.0F)
            {
                f = 1.0F;
            }

            f = friction / f;
            strafe = strafe * f;
            forward = forward * f;
            float f1 = ExtMath.sin(this.rotYaw * (float)Math.PI / 180.0F);
            float f2 = ExtMath.cos(this.rotYaw * (float)Math.PI / 180.0F);
            this.motionX += (double)(strafe * f2 - forward * f1);
            this.motionZ += (double)(forward * f2 + strafe * f1);
        }
    }

    public int getBrightnessForRender(float partialTicks)
    {
        BlockPos blockpos = new BlockPos(this.posX, this.posY + (double)this.getEyeHeight(), this.posZ);
        return this.worldObj.isBlockLoaded(blockpos) ? this.worldObj.getCombinedLight(blockpos, 0) : 0;
    }

    /**
     * Gets how bright this entity is.
     */
    public float getBrightness(float partialTicks)
    {
        BlockPos blockpos = new BlockPos(this.posX, this.posY + (double)this.getEyeHeight(), this.posZ);
        return this.worldObj.isBlockLoaded(blockpos) ? this.worldObj.getLightBrightness(blockpos) : 0.0F;
    }

    /**
     * Sets the reference to the World object.
     */
    public void setWorld(World worldIn)
    {
        this.worldObj = worldIn;
    }

    /**
     * Sets the entity's position and rotation.
     */
    public void setPositionAndRotation(double x, double y, double z, float yaw, float pitch)
    {
        this.prevX = this.posX = x;
        this.prevY = this.posY = y;
        this.prevZ = this.posZ = z;
        this.prevYaw = this.rotYaw = yaw;
        this.prevPitch = this.rotPitch = pitch;
        double d0 = (double)(this.prevYaw - yaw);

        if (d0 < -180.0D)
        {
            this.prevYaw += 360.0F;
        }

        if (d0 >= 180.0D)
        {
            this.prevYaw -= 360.0F;
        }

        this.setPosition(this.posX, this.posY, this.posZ);
        this.setRotation(yaw, pitch);
    }

    public void moveToBlockPosAndAngles(BlockPos pos, float rotationYawIn, float rotationPitchIn)
    {
        this.setLocationAndAngles((double)pos.getX() + 0.5D, (double)pos.getY(), (double)pos.getZ() + 0.5D, rotationYawIn, rotationPitchIn);
    }

    /**
     * Sets the location and Yaw/Pitch of an entity in the world
     */
    public void setLocationAndAngles(double x, double y, double z, float yaw, float pitch)
    {
        this.lastTickPosX = this.prevX = this.posX = x;
        this.lastTickPosY = this.prevY = this.posY = y;
        this.lastTickPosZ = this.prevZ = this.posZ = z;
        this.rotYaw = yaw;
        this.rotPitch = pitch;
        this.setPosition(this.posX, this.posY, this.posZ);
    }

    /**
     * Returns the distance to the entity. Args: entity
     */
    public float getDistanceToEntity(Entity entityIn)
    {
        float f = (float)(this.posX - entityIn.posX);
        float f1 = (float)(this.posY - entityIn.posY);
        float f2 = (float)(this.posZ - entityIn.posZ);
        return ExtMath.sqrtf(f * f + f1 * f1 + f2 * f2);
    }

    /**
     * Gets the squared distance to the position. Args: x, y, z
     */
    public double getDistanceSq(double x, double y, double z)
    {
        double d0 = this.posX - x;
        double d1 = this.posY - y;
        double d2 = this.posZ - z;
        return d0 * d0 + d1 * d1 + d2 * d2;
    }

    public double getDistanceSq(BlockPos pos)
    {
        return pos.distanceSq(this.posX, this.posY, this.posZ);
    }

    public double getDistanceSqToCenter(BlockPos pos)
    {
        return pos.distanceSqToCenter(this.posX, this.posY, this.posZ);
    }

    /**
     * Gets the distance to the position. Args: x, y, z
     */
    public double getDistance(double x, double y, double z)
    {
        double d0 = this.posX - x;
        double d1 = this.posY - y;
        double d2 = this.posZ - z;
        return (double)ExtMath.sqrtd(d0 * d0 + d1 * d1 + d2 * d2);
    }

    /**
     * Returns the squared distance to the entity. Args: entity
     */
    public double getDistanceSqToEntity(Entity entityIn)
    {
        double d0 = this.posX - entityIn.posX;
        double d1 = this.posY - entityIn.posY;
        double d2 = this.posZ - entityIn.posZ;
        return d0 * d0 + d1 * d1 + d2 * d2;
    }

    /**
     * Called by a player entity when they collide with an entity
     */
    public void onCollideWithPlayer(EntityNPC entityIn)
    {
    }

    /**
     * Applies a velocity to each of the entities pushing them away from each other. Args: entity
     */
    public void applyEntityCollision(Entity entityIn)
    {
        if (entityIn.passenger != this && entityIn.vehicle != this)
        {
            if (!entityIn.noClip && !this.noClip)
            {
                double d0 = entityIn.posX - this.posX;
                double d1 = entityIn.posZ - this.posZ;
            	double v1 = d0 < 0.0D ? -d0 : d0;
            	double v2 = d1 < 0.0D ? -d1 : d1;
                double d2 = v1 > v2 ? v1 : v2;
//                double d2 = ExtMath.abs_max(d0, d1);

                if (d2 >= 0.009999999776482582D)
                {
                    d2 = (double)ExtMath.sqrtd(d2);
                    d0 = d0 / d2;
                    d1 = d1 / d2;
                    double d3 = 1.0D / d2;

                    if (d3 > 1.0D)
                    {
                        d3 = 1.0D;
                    }

                    d0 = d0 * d3;
                    d1 = d1 * d3;
                    d0 = d0 * 0.05000000074505806D;
                    d1 = d1 * 0.05000000074505806D;
                    d0 = d0 * (double)(1.0F - this.collisionReduction);
                    d1 = d1 * (double)(1.0F - this.collisionReduction);

                    if (this.passenger == null)
                    {
                        this.addVelocity(-d0, 0.0D, -d1);
                    }

                    if (entityIn.passenger == null)
                    {
                        entityIn.addVelocity(d0, 0.0D, d1);
                    }
                }
            }
        }
    }

    /**
     * Adds to the current velocity of the entity. Args: x, y, z
     */
    public void addVelocity(double x, double y, double z)
    {
        this.motionX += x;
        this.motionY += y;
        this.motionZ += z;
        this.isAirBorne = true;
    }
    
    public void addKnockback(double x, double y, double z)
    {
        float mult = this.worldObj.client ? 1.0f : Vars.knockback;
        this.addVelocity(x * mult, y * mult, z * mult);
    }

    /**
     * Sets that this entity has been attacked.
     */
    protected void setBeenAttacked()
    {
        this.veloChanged = true;
    }

    /**
     * Called when the entity is attacked.
     */
    public boolean attackEntityFrom(DamageSource source, int amount)
    {
//        if (this.isEntityInvulnerable(source))
//        {
//            return false;
//        }
//        else
//        {
        this.setBeenAttacked();
        return false;
//        }
    }

    /**
     * interpolated look vector
     */
    public Vec3 getLook(float partialTicks)
    {
        if (partialTicks == 1.0F)
        {
            return this.getVectorForRotation(this.rotPitch, this.rotYaw);
        }
        else
        {
            float f = this.prevPitch + (this.rotPitch - this.prevPitch) * partialTicks;
            float f1 = this.prevYaw + (this.rotYaw - this.prevYaw) * partialTicks;
            return this.getVectorForRotation(f, f1);
        }
    }

    /**
     * Creates a Vec3 using the pitch and yaw of the entities rotation.
     */
    protected final Vec3 getVectorForRotation(float pitch, float yaw)
    {
        float f = ExtMath.cos(-yaw * 0.017453292F - (float)Math.PI);
        float f1 = ExtMath.sin(-yaw * 0.017453292F - (float)Math.PI);
        float f2 = -ExtMath.cos(-pitch * 0.017453292F);
        float f3 = ExtMath.sin(-pitch * 0.017453292F);
        return new Vec3((double)(f1 * f2), (double)f3, (double)(f * f2));
    }

    public Vec3 getPositionEyes(float partialTicks)
    {
        if (partialTicks == 1.0F)
        {
            return new Vec3(this.posX, this.posY + (double)this.getEyeHeight(), this.posZ);
        }
        else
        {
            double d0 = this.prevX + (this.posX - this.prevX) * (double)partialTicks;
            double d1 = this.prevY + (this.posY - this.prevY) * (double)partialTicks + (double)this.getEyeHeight();
            double d2 = this.prevZ + (this.posZ - this.prevZ) * (double)partialTicks;
            return new Vec3(d0, d1, d2);
        }
    }

    public HitPosition rayTrace(double blockReachDistance, float partialTicks)
    {
        Vec3 vec3 = this.getPositionEyes(partialTicks);
        Vec3 vec31 = this.getLook(partialTicks);
        Vec3 vec32 = vec3.addVector(vec31.xCoord * blockReachDistance, vec31.yCoord * blockReachDistance, vec31.zCoord * blockReachDistance);
        return this.worldObj.rayTraceBlocks(vec3, vec32, false, false, true);
    }

    /**
     * Returns true if other Entities should be prevented from moving through this Entity.
     */
    public boolean canBeCollidedWith()
    {
        return false;
    }

    /**
     * Returns true if this entity should push and be pushed by other entities when colliding.
     */
    public boolean canBePushed()
    {
        return false;
    }

    public boolean isInRangeToRender3d(double x, double y, double z)
    {
        double d0 = this.posX - x;
        double d1 = this.posY - y;
        double d2 = this.posZ - z;
        double d3 = d0 * d0 + d1 * d1 + d2 * d2;
        return this.isInRangeToRenderDist(d3);
    }

    /**
     * Checks if the entity is in range to render by using the past in distance and comparing it to its average edge
     * length * 64 * renderDistanceWeight Args: distance
     */
    public boolean isInRangeToRenderDist(double distance)
    {
        double d0 = this.getEntityBoundingBox().getAverageEdgeLength();

        if (Double.isNaN(d0))
        {
            d0 = 1.0D;
        }

        d0 = d0 * 64.0D * this.renderDistWeight;
        return distance < d0 * d0;
    }

    /**
     * Like writeOptional but does not check if the entity is ridden. Used for saving ridden entities with their
     * riders.
     */
    public boolean writeMount(TagObject tag)
    {
        String s = EntityRegistry.getEntityString(this);

        if (!this.dead && !this.isPlayer() && s != null)
        {
            tag.setString("id", s);
            this.writeTags(tag);
            return true;
        }
        else
        {
            return false;
        }
    }

    /**
     * Either write this entity to the tag given and return true, or return false without doing anything. If this
     * returns false the entity is not saved on disk. Ridden entities return false here as they are saved with their
     * rider.
     */
    public boolean writeOptional(TagObject tag)
    {
        String s = EntityRegistry.getEntityString(this);

        if (!this.dead && !this.isPlayer() && s != null && (this.passenger == null || this.passenger.isPlayer()))
        {
            tag.setString("id", s);
            this.writeTags(tag);
            return true;
        }
        else
        {
            return false;
        }
    }
    
//    public void setTag(String tag) {
//    	this.tag = tag.isEmpty() ? null : tag;
//    }
//    
//    public String getTag() {
//    	return this.tag;
//    }
//    
//    public boolean hasTag() {
//    	return this.tag != null;
//    }
    
//    public void setPersistentId(long id) {
//    	this.persistentId = id;
//    }
//    
//    public long getPersistentId() {
//    	return this.persistentId;
//    }
//    
//    public boolean hasPersistentId() {
//    	return this.persistentId != 0L;
//    }

    /**
     * Save the entity to tags (calls an abstract helper method to write extra data)
     */
    public void writeTags(TagObject tag)
    {
//    	if(this.persistentId != 0L) {
//    		tagCompund.setLong("PersistID", this.persistentId);
//    	}
//    	if(this.tag != null) {
//    		tagCompund.setString("ObjectTag", this.tag);
//    	}

        tag.setDouble("PosX", this.posX);
        tag.setDouble("PosY", this.posY);
        tag.setDouble("PosZ", this.posZ);
        tag.setDouble("MotionX", this.motionX);
        tag.setDouble("MotionY", this.motionY);
        tag.setDouble("MotionZ", this.motionZ);
        tag.setFloat("Yaw", this.rotYaw);
        tag.setFloat("Pitch", this.rotPitch);
        tag.setFloat("FallDistance", this.fallDistance);
        tag.setShort("Fire", (short)this.fire);
//        tagCompund.setShort("Air", (short)this.getAir());
        tag.setBool("OnGround", this.onGround);
//        tagCompund.setBoolean("Invulnerable", this.invulnerable);
//        tagCompund.setInteger("PortalCooldown", this.portalTimer);

        if (this.getCustomNameTag() != null && this.getCustomNameTag().length() > 0)
        {
            tag.setString("CustomName", this.getCustomNameTag());
//            tagCompund.setBoolean("CustomNameVisible", this.getAlwaysRenderNameTag());
        }

//        if (this.isSilent())
//        {
//            tagCompund.setBoolean("Silent", this.isSilent());
//        }
        
        if(this.ignoreFall)
        	tag.setBool("IgnoreFall", this.ignoreFall);

        this.writeEntity(tag);

        if (this.vehicle != null && !(this.isPlayer()))
        {
            TagObject mount = new TagObject();

            if (this.vehicle.writeMount(mount))
            {
                tag.setObject("Riding", mount);
            }
        }
    }

    /**
     * Reads the entity from tags (calls an abstract helper method to read specialized data)
     */
    public void readTags(TagObject tag)
    {
//    	if(tagCompund.hasLong("PersistID"))
//        	this.setPersistentId(tagCompund.getLong("PersistID"));
//    	this.setTag(tagCompund.getString("ObjectTag"));
    	
        this.motionX = tag.getDouble("MotionX");
        this.motionY = tag.getDouble("MotionY");
        this.motionZ = tag.getDouble("MotionZ");

        if (Math.abs(this.motionX) > 10.0D)
        {
            this.motionX = 0.0D;
        }

        if (Math.abs(this.motionY) > 10.0D)
        {
            this.motionY = 0.0D;
        }

        if (Math.abs(this.motionZ) > 10.0D)
        {
            this.motionZ = 0.0D;
        }

        this.prevX = this.lastTickPosX = this.posX = tag.getDouble("PosX");
        this.prevY = this.lastTickPosY = this.posY = tag.getDouble("PosY");
        this.prevZ = this.lastTickPosZ = this.posZ = tag.getDouble("PosZ");
        this.prevYaw = this.rotYaw = tag.getFloat("Yaw");
        this.prevPitch = this.rotPitch = tag.getFloat("Pitch");
        this.setRotationYawHead(this.rotYaw);
        this.setRenderYawOffset(this.rotYaw);
        this.fallDistance = tag.getFloat("FallDistance");
        this.fire = tag.getShort("Fire");
//        this.setAir(tagCompund.getShort("Air"));
        this.onGround = tag.getBool("OnGround");
//        this.invulnerable = tagCompund.getBoolean("Invulnerable");
//        this.portalTimer = tagCompund.getInteger("PortalCooldown");

        this.setPosition(this.posX, this.posY, this.posZ);
        this.setRotation(this.rotYaw, this.rotPitch);

        if (tag.hasString("CustomName") && tag.getString("CustomName").length() > 0)
        {
            this.setCustomNameTag(tag.getString("CustomName"));
        }

//        this.setAlwaysRenderNameTag(tagCompund.getBoolean("CustomNameVisible"));
//        this.setSilent(tagCompund.getBoolean("Silent"));
        this.ignoreFall = tag.getBool("IgnoreFall");
        this.readEntity(tag);

        if (this.shouldSetPosAfterLoading())
        {
            this.setPosition(this.posX, this.posY, this.posZ);
        }
    }

    protected boolean shouldSetPosAfterLoading()
    {
        return true;
    }

//    /**
//     * Returns the string that identifies this Entity's class
//     */
//    protected final String getEntityString()
//    {
//        return EntityRegistry.getEntityString(this);
//    }

    /**
     * (abstract) Protected helper method to read subclass entity data from tags.
     */
    protected abstract void readEntity(TagObject tag);

    /**
     * (abstract) Protected helper method to write subclass entity data to tags.
     */
    protected abstract void writeEntity(TagObject tag);

    public void onChunkLoad()
    {
    }

    public EntityItem dropItem(Item itemIn, int size)
    {
        return this.dropItemWithOffset(itemIn, size, 0.0F);
    }

    public EntityItem dropItemWithOffset(Item itemIn, int size, float offsetY)
    {
        return this.entityDropItem(new ItemStack(itemIn, size), offsetY);
    }

    /**
     * Drops an item at the position of the entity.
     */
    public EntityItem entityDropItem(ItemStack itemStackIn, float offsetY)
    {
        if (itemStackIn.size != 0 && itemStackIn.getItem() != null)
        {
            EntityItem entityitem = new EntityItem(this.worldObj, this.posX, this.posY + (double)offsetY, this.posZ, itemStackIn);
            entityitem.setDefaultPickupDelay();
            this.worldObj.spawnEntityInWorld(entityitem);
            return entityitem;
        }
        else
        {
            return null;
        }
    }

    /**
     * Checks whether target entity is alive.
     */
    public boolean isEntityAlive()
    {
        return !this.dead;
    }

    /**
     * Checks if this entity is inside of an opaque block
     */
    public boolean isEntityInsideOpaqueBlock()
    {
        if (this.noClip)
        {
            return false;
        }
        else
        {
            BlockPos.MutableBlockPos blockpos$mutableblockpos = new BlockPos.MutableBlockPos(Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE);

            for (int i = 0; i < 8; ++i)
            {
                int j = ExtMath.floord(this.posY + (double)(((float)((i >> 0) % 2) - 0.5F) * 0.1F) + (double)this.getEyeHeight());
                int k = ExtMath.floord(this.posX + (double)(((float)((i >> 1) % 2) - 0.5F) * this.width * 0.8F));
                int l = ExtMath.floord(this.posZ + (double)(((float)((i >> 2) % 2) - 0.5F) * this.width * 0.8F));

                if (blockpos$mutableblockpos.getX() != k || blockpos$mutableblockpos.getY() != j || blockpos$mutableblockpos.getZ() != l)
                {
                    blockpos$mutableblockpos.set(k, j, l);

                    if (this.worldObj.getState(blockpos$mutableblockpos).getBlock().isVisuallyOpaque())
                    {
                        return true;
                    }
                }
            }

            return false;
        }
    }

    /**
     * First layer of player interaction
     */
    public boolean interactFirst(EntityNPC playerIn)
    {
        return false;
    }

    /**
     * Returns a boundingBox used to collide the entity with other entities and blocks. This enables the entity to be
     * pushable on contact, like boats or minecarts.
     */
    public BoundingBox getCollisionBox(Entity entityIn)
    {
        return null;
    }

    /**
     * Handles updating while being ridden by an entity
     */
    public void updateRidden()
    {
        if (this.vehicle.dead)
        {
            this.vehicle = null;
        }
        else
        {
            this.motionX = 0.0D;
            this.motionY = 0.0D;
            this.motionZ = 0.0D;
            this.onUpdate();

            if (this.vehicle != null)
            {
                this.vehicle.updateRiderPosition();
                this.riderYawDelta += (double)(this.vehicle.rotYaw - this.vehicle.prevYaw);

                for (this.riderPitchDelta += (double)(this.vehicle.rotPitch - this.vehicle.prevPitch); this.riderYawDelta >= 180.0D; this.riderYawDelta -= 360.0D)
                {
                    ;
                }

                while (this.riderYawDelta < -180.0D)
                {
                    this.riderYawDelta += 360.0D;
                }

                while (this.riderPitchDelta >= 180.0D)
                {
                    this.riderPitchDelta -= 360.0D;
                }

                while (this.riderPitchDelta < -180.0D)
                {
                    this.riderPitchDelta += 360.0D;
                }

                double d0 = this.riderYawDelta * 0.5D;
                double d1 = this.riderPitchDelta * 0.5D;
                float f = 10.0F;

                if (d0 > (double)f)
                {
                    d0 = (double)f;
                }

                if (d0 < (double)(-f))
                {
                    d0 = (double)(-f);
                }

                if (d1 > (double)f)
                {
                    d1 = (double)f;
                }

                if (d1 < (double)(-f))
                {
                    d1 = (double)(-f);
                }

                this.riderYawDelta -= d0;
                this.riderPitchDelta -= d1;
            }
        }
    }

    public void updateRiderPosition()
    {
        if (this.passenger != null)
        {
            this.passenger.setPosition(this.posX, this.posY + this.getMountedYOffset() + this.passenger.getYOffset(), this.posZ);
        }
    }

    /**
     * Returns the Y Offset of this entity.
     */
    public double getYOffset()
    {
        return 0.0D;
    }

    /**
     * Returns the Y offset from the entity's position for any entity riding this one.
     */
    public double getMountedYOffset()
    {
        return (double)this.height * 0.75D;
    }
    
    public void teleport(double x, double y, double z, float yaw, float pitch, int dimension) {
		x = ExtMath.clampd(x, -World.MAX_SIZE + 1, World.MAX_SIZE - 1);
		z = ExtMath.clampd(z, -World.MAX_SIZE + 1, World.MAX_SIZE - 1);
    	this.unmount();
    	Entity entity = this;
    	if(this.worldObj.dimension.getDimensionId() != dimension) {
    		entity = this.travelToDimension(dimension, new BlockPos(x, y, z), yaw, pitch, null);
    	}
    	entity.setLocationAndAngles(x, y, z, yaw, pitch);
    	entity.setRotationYawHead(yaw);
    }
    
//    protected void setY(double y) {
//    	this.unmount();
//    	this.setLocationAndAngles(this.posX, y, this.posZ, this.rotYaw, this.rotPitch);
//    	this.setRotationYawHead(this.rotYaw);
//    }
    
    public final void teleport(Position pos) {
    	this.teleport(pos.x(), pos.y(), pos.z(), pos.yaw(), pos.pitch(), pos.dim());
    }
    
    public final void teleport(BlockPos pos, int dim) {
    	this.teleport(((double)pos.getX()) + 0.5, (double)pos.getY(), ((double)pos.getZ()) + 0.5, this.rotYaw, this.rotPitch, dim);
    }
    
//    public final Position getLocation() {
//    	return new Position(this);
//    }

    /**
     * Called when a player mounts an entity. e.g. mounts a pig, mounts a boat.
     */
    public void mountEntity(Entity entityIn)
    {
        this.riderPitchDelta = 0.0D;
        this.riderYawDelta = 0.0D;

        if (entityIn == null)
        {
            if (this.vehicle != null)
            {
                this.setLocationAndAngles(this.vehicle.posX, this.vehicle.getEntityBoundingBox().minY + (double)this.vehicle.height, this.vehicle.posZ, this.rotYaw, this.rotPitch);
                this.vehicle.passenger = null;
            }

            this.vehicle = null;
        }
        else
        {
            if (this.vehicle != null)
            {
                this.vehicle.passenger = null;
            }

            if (entityIn != null)
            {
                for (Entity entity = entityIn.vehicle; entity != null; entity = entity.vehicle)
                {
                    if (entity == this)
                    {
                        return;
                    }
                }
            }

            this.vehicle = entityIn;
            entityIn.passenger = this;
        }
    }

    public void setClientPosition(double x, double y, double z, float yaw, float pitch, boolean teleport)
    {
        this.setPosition(x, y, z);
        this.setRotation(yaw, pitch);
        List<BoundingBox> list = this.worldObj.getCollidingBoundingBoxes(this, this.getEntityBoundingBox().contract(0.03125D, 0.0D, 0.03125D));

        if (!list.isEmpty())
        {
            double d0 = 0.0D;

            for (BoundingBox axisalignedbb : list)
            {
                if (axisalignedbb.maxY > d0)
                {
                    d0 = axisalignedbb.maxY;
                }
            }

            y = y + (d0 - this.getEntityBoundingBox().minY);
            this.setPosition(x, y, z);
        }
    }

    public float getCollisionBorderSize()
    {
        return 0.1F;
    }

    /**
     * returns a (normalized) vector of where this entity is looking
     */
    public Vec3 getLookVec()
    {
        return null;
    }

    /**
     * Marks the entity as being inside a portal, activating teleportation logic in onEntityUpdate() in the following
     * tick(s).
     *  
     * @param pos The postion of the portal that the entity is in
     */
    public void setPortal(int dim)
    {
//        if (this.portalTimer > 0)
//        {
//            this.portalTimer = this.getPortalCooldown();
//        }
//        else
//        {
//            if (!this.worldObj.client && !pos.equals(this.portalPos))
//            {
//                this.portalPos = pos;
//                BlockPattern.PatternHelper blockpattern$patternhelper = Blocks.portal.func_181089_f(this.worldObj, pos);
//                double d0 = blockpattern$patternhelper.getFinger().getAxis() == EnumFacing.Axis.X ? (double)blockpattern$patternhelper.getPos().getZ() : (double)blockpattern$patternhelper.getPos().getX();
//                double d1 = blockpattern$patternhelper.getFinger().getAxis() == EnumFacing.Axis.X ? this.posZ : this.posX;
//                d1 = Math.abs(MathHelper.func_181160_c(d1 - (double)(blockpattern$patternhelper.getFinger().rotateY().getAxisDirection() == EnumFacing.AxisDirection.NEGATIVE ? 1 : 0), d0, d0 - (double)blockpattern$patternhelper.getPalmLength()));
//                double d2 = MathHelper.func_181160_c(this.posY - 1.0D, (double)blockpattern$patternhelper.getPos().getY(), (double)(blockpattern$patternhelper.getPos().getY() - blockpattern$patternhelper.getThumbLength()));
//                this.portalVec = new Vec3(d1, d2, 0.0D);
//                this.portalDir = blockpattern$patternhelper.getFinger();
//            }
//            
        if(!this.worldObj.client)
        	this.unmount();
        this.inPortal = PortalType.values()[dim];
//        }
    }
    
    public void setFlatPortal()
    {
//        if (this.portalTimer > 0)
//        {
//            this.portalTimer = this.getPortalCooldown();
//        }
//        else
//        {
    	if(!this.worldObj.client)
    		this.unmount();
        this.inPortal = PortalType.FLOOR;
//        }
    }
    
    public void unmount()
    {
    	if(this.passenger != null) {
    		this.passenger.mountEntity(null);
    	}
        this.mountEntity(null);
    }

//    /**
//     * Return the amount of cooldown before this entity can use a portal again.
//     */
//    public int getPortalCooldown()
//    {
//        return 40;
//    }

    /**
     * Sets the velocity to the args. Args: x, y, z
     */
    public void setVelocity(double x, double y, double z)
    {
        this.motionX = x;
        this.motionY = y;
        this.motionZ = z;
    }

    public void handleStatusUpdate(byte id)
    {
    }

    /**
     * Setups the entity to do the hurt animation. Only used by packets in multiplayer.
     */
    public void performHurtAnimation()
    {
    }

    /**
     * returns the inventory of this entity (only used in EntityNPCMP it seems)
     */
    public ItemStack[] getInventory()
    {
        return null;
    }

    /**
     * Sets the held item, or an armor slot. Slot 0 is held item. Slot 1-4 is armor. Params: Item, slot
     */
    public void setItem(int slot, ItemStack stack)
    {
    }

    /**
     * Returns true if the entity is on fire. Used by render to add the fire effect on rendering.
     */
    public boolean isBurning()
    {
        boolean flag = this.worldObj != null && this.worldObj.client;
        return !this.isImmuneToFire() && (this.fire > 0 || flag && this.getFlag(0));
    }

    /**
     * Returns true if the entity is riding another entity, used by render to rotate the legs to be in 'sit' position
     * for players.
     */
    public boolean isRiding()
    {
        return this.vehicle != null;
    }

    /**
     * Returns if this entity is sneaking.
     */
    public boolean isSneaking()
    {
        return this.getFlag(1);
    }

    /**
     * Sets the sneaking flag.
     */
    public void setSneaking(boolean sneaking)
    {
        this.setFlag(1, sneaking);
    }

    /**
     * Get if the Entity is sprinting.
     */
    public boolean isSprinting()
    {
        return this.getFlag(2);
    }

    /**
     * Set sprinting switch for Entity.
     */
    public void setSprinting(boolean sprinting)
    {
        this.setFlag(2, sprinting);
    }

//    public boolean isInvisible()
//    {
//        return false;
//    }

//    public void setInvisible(boolean invisible)
//    {
//        this.setFlag(3, invisible);
//    }

    /**
     * Returns true if the flag is active for the entity. Known flags: 0) is burning; 1) is sneaking; 2) is riding
     * something; 3) is sprinting; 4) is eating
     */
    protected boolean getFlag(int flag)
    {
        return (this.dataWatcher.getWatchableObjectByte(0) & 1 << flag) != 0;
    }

    /**
     * Enable or disable a entity flag, see getEntityFlag to read the know flags.
     */
    protected void setFlag(int flag, boolean set)
    {
        byte b0 = this.dataWatcher.getWatchableObjectByte(0);

        if (set)
        {
            this.dataWatcher.updateObject(0, Byte.valueOf((byte)(b0 | 1 << flag)));
        }
        else
        {
            this.dataWatcher.updateObject(0, Byte.valueOf((byte)(b0 & ~(1 << flag))));
        }
    }

//    public int getAir()
//    {
//        return this.dataWatcher.getWatchableObjectShort(1);
//    }
//
//    public void setAir(int air)
//    {
//        this.dataWatcher.updateObject(1, Short.valueOf((short)air));
//    }

    /**
     * Called when a lightning bolt hits the entity.
     */
    public void onStruckByLightning(EntityLightning bolt)
    {
    	if((this.worldObj.client || Vars.damageLightning) && bolt.damage > 0)
    		this.attackEntityFrom(DamageSource.causeLightningDamage(bolt.summoner), this.worldObj.rand.range(bolt.damage, ((bolt.damage * 3) / 2)));
    	if(bolt.fire) {
	        ++this.fire;
	        if(this.fire == 0)
	            this.setFire(8);
    	}
    }

    /**
     * This method gets called when the entity kills another one.
     */
    public void onKillEntity(EntityLiving entityLivingIn)
    {
    }

	private boolean isBlockFullCube(BlockPos pos) {
		State state = this.worldObj.getState(pos);
		BoundingBox bb = state.getBlock().getCollisionBoundingBox(this.worldObj, pos, state);
		return bb != null && bb.getAverageEdgeLength() >= 1.0D;
	}

    protected boolean pushOutOfBlocks(double x, double y, double z)
    {
        BlockPos blockpos = new BlockPos(x, y, z);
        double d0 = x - (double)blockpos.getX();
        double d1 = y - (double)blockpos.getY();
        double d2 = z - (double)blockpos.getZ();
        List<BoundingBox> list = this.worldObj.getCollisionBoxes(this.getEntityBoundingBox());

        if (list.isEmpty() && !this.isBlockFullCube(blockpos))
        {
            return false;
        }
        else
        {
            int i = 3;
            double d3 = 9999.0D;

            if (!this.isBlockFullCube(blockpos.west()) && d0 < d3)
            {
                d3 = d0;
                i = 0;
            }

            if (!this.isBlockFullCube(blockpos.east()) && 1.0D - d0 < d3)
            {
                d3 = 1.0D - d0;
                i = 1;
            }

            if (!this.isBlockFullCube(blockpos.up()) && 1.0D - d1 < d3)
            {
                d3 = 1.0D - d1;
                i = 3;
            }

            if (!this.isBlockFullCube(blockpos.north()) && d2 < d3)
            {
                d3 = d2;
                i = 4;
            }

            if (!this.isBlockFullCube(blockpos.south()) && 1.0D - d2 < d3)
            {
                d3 = 1.0D - d2;
                i = 5;
            }

            float f = this.rand.floatv() * 0.2F + 0.1F;

            if (i == 0)
            {
                this.motionX = (double)(-f);
            }

            if (i == 1)
            {
                this.motionX = (double)f;
            }

            if (i == 3)
            {
                this.motionY = (double)f;
            }

            if (i == 4)
            {
                this.motionZ = (double)(-f);
            }

            if (i == 5)
            {
                this.motionZ = (double)f;
            }

            return true;
        }
    }

    /**
     * Sets the Entity inside a web block.
     */
    public void setInWeb()
    {
        this.inWeb = true;
        this.fallDistance = 0.0F;
    }

    /**
     * Get the name of this object. For players this returns their username
     */
    public String getTypeName()
    {
        if (this.hasCustomName())
        {
            return this.getCustomNameTag();
        }
        else
        {
            String s = EntityRegistry.getEntityString(this);

            if (s == null)
            {
                s = "generic";
            }

            return EntityRegistry.getEntityName(s);
        }
    }

    /**
     * Return the Entity parts making up this Entity (currently only for dragons)
     */
    public Entity[] getParts()
    {
        return null;
    }

    /**
     * Returns true if Entity argument is equal to this Entity
     */
    public boolean isEntityEqual(Entity entityIn)
    {
        return this == entityIn;
    }

    public float getRotationYawHead()
    {
        return 0.0F;
    }

    /**
     * Sets the head's yaw rotation of the entity.
     */
    public void setRotationYawHead(float rotation)
    {
    }

    /**
     * Set the render yaw offset
     *  
     * @param offset The render yaw offset
     */
    public void setRenderYawOffset(float offset)
    {
    }

    /**
     * If returns false, the item will not inflict any damage against entities.
     */
    public boolean canAttackWithItem()
    {
        return true;
    }

    /**
     * Called when a player attacks an entity. If this returns true the attack will not happen.
     */
    public boolean hitByEntity(Entity entityIn)
    {
        return false;
    }

//    public String toString()
//    {
//        return String.format("%s[\'%s\'/%d, l=\'%s\', x=%.2f, y=%.2f, z=%.2f]", this.getClass().getSimpleName(), this.getName(), this.eid, this.worldObj == null ? "~NULL~" : this.worldObj.dimension.getDimensionId(), this.posX, this.posY, this.posZ);
//    }

//    public final boolean isEntityInvulnerable(DamageSource source)
//    {
//        return false; // this.invulnerable && source != DamageSource.outOfWorld; // && !source.isCreativePlayer();
//    }

    /**
     * Sets this entity's location and angles to the location and angles of the passed in entity.
     */
    public void copyLocationAndAnglesFrom(Entity entityIn)
    {
        this.setLocationAndAngles(entityIn.posX, entityIn.posY, entityIn.posZ, entityIn.rotYaw, entityIn.rotPitch);
    }

    /**
     * Prepares this entity in new dimension by copying data from entity in old dimension
     */
    public void copyDataFromOld(Entity entityIn)
    {
        TagObject tag = new TagObject();
        entityIn.writeTags(tag);
        this.readTags(tag);
//        this.portalTimer = entityIn.portalTimer;
//        this.portalPos = entityIn.portalPos;
//        this.portalVec = entityIn.portalVec;
//        this.portalDir = entityIn.portalDir;
    }

    /**
     * Teleports the entity to another dimension. Params: Dimension number to teleport to
     */
    public Entity travelToDimension(int dimensionId, BlockPos pos, float yaw, float pitch, PortalType portal)
    {
        if (!this.worldObj.client && !this.dead)
        {
//            this.worldObj.profiler.start("changeDimension");
//            Server server = Server.getServer();
//            int oldDim = this.dimension;
            AWorldServer worldserver = (AWorldServer)this.worldObj; // server.getWorld(oldDim);
            AWorldServer worldserver1 = worldserver.getOtherWorld(dimensionId);
//            this.dimension = dimensionId;

//            if (i == 1 && dimensionId == 1)
//            {
//                worldserver1 = server.getWorld(0);
//                this.dimension = 0;
//            }

            this.worldObj.removeEntity(this);
            this.dead = false;
//            this.worldObj.profiler.start("reposition");
            worldserver.placeInDimension(this, worldserver, worldserver1, pos, portal);
    		if(this.isEntityAlive())
				worldserver1.updateEntity(this, false);
//            this.worldObj.profiler.next("reloading");
            Entity entity = EntityRegistry.createEntityByName(EntityRegistry.getEntityString(this), worldserver1);

            if (entity != null)
            {
                entity.copyDataFromOld(this);

                if(pos != null) {
                    entity.moveToBlockPosAndAngles(pos, yaw, pitch);
                }
//                else if (oldDim != 1 && dimensionId == 1)
//                {
//                    BlockPos blockpos = this.worldObj.getTopSolidOrLiquidBlock(worldserver1.getSpawnPoint());
//                    entity.moveToBlockPosAndAngles(blockpos, entity.rotationYaw, entity.rotationPitch);
//                }

                worldserver1.spawnEntityInWorld(entity);
            }

            this.dead = true;
//            this.worldObj.profiler.end();
            worldserver.resetUpdateEntityTick();
            worldserver1.resetUpdateEntityTick();
//            this.worldObj.profiler.end();
            return entity;
        }
        return this;
    }

    /**
     * Explosion resistance of a block relative to this entity
     */
    public float getExplosionResistance(Explosion explosionIn, World worldIn, BlockPos pos, State blockStateIn)
    {
        return blockStateIn.getBlock().getExplosionResistance(this);
    }

    public boolean verifyExplosion(Explosion explosionIn, World worldIn, BlockPos pos, State blockStateIn, float p_174816_5_)
    {
        return true;
    }

    /**
     * The maximum height from where the entity is alowed to jump (used in pathfinder)
     */
    public int getMaxFallHeight()
    {
        return 3;
    }

//    public Vec3 getLastPortal()
//    {
//        return this.portalVec != null ? this.portalVec : new Vec3(0.0d, 0.0d, 0.0d);
//    }
//
//    public EnumFacing getTeleportDirection()
//    {
//        return this.portalDir != null ? this.portalDir : EnumFacing.EAST;
//    }

    /**
     * Return whether this entity should NOT trigger a pressure plate or a tripwire.
     */
    public boolean doesEntityNotTriggerPressurePlate()
    {
        return false;
    }

    /**
     * Return whether this entity should be rendered as on fire.
     */
    public boolean canRenderOnFire()
    {
        return this.isBurning();
    }

    public boolean isPushedByWater()
    {
        return true;
    }

    /**
     * Get the formatted ChatComponent that will be used for the sender's username in chat
     */
    public String getName()
    {
    	return this.getTypeName();
//        this.formatComponent(comp);
//        return comp;
    }
    
    public String getDisplayName()
    {
    	return this.hasCustomName() ? this.getName() : null;
    }
    
    public final String getCommandName()
    {
    	return TextColor.stripCodes(this.getName());
    }
    
    public final String getColoredName(TextColor reset)
    {
    	return TextColor.DGRAY + "[" + this.getName() + TextColor.DGRAY + "]" + reset;
    }
    
//    public Text formatComponent(Text comp) {
//    	comp.setReplacement("/tp #" + this.getId()).setEntityInfo(this).setInsertion("#" + this.getId());
//    	return comp;
//    }

    /**
     * Sets the custom name tag for this entity
     */
    public void setCustomNameTag(String name)
    {
        this.dataWatcher.updateObject(1, name);
    }

    public String getCustomNameTag()
    {
        return this.dataWatcher.getWatchableObjectString(1);
    }

    /**
     * Returns true if this thing is named
     */
    public boolean hasCustomName()
    {
        return this.dataWatcher.getWatchableObjectString(1).length() > 0;
    }

//    public void setAlwaysRenderNameTag(boolean alwaysRenderNameTag)
//    {
//        this.dataWatcher.updateObject(3, Byte.valueOf((byte)(alwaysRenderNameTag ? 1 : 0)));
//    }
//
//    public boolean getAlwaysRenderNameTag()
//    {
//        return this.dataWatcher.getWatchableObjectByte(3) == 1;
//    }

    /**
     * Sets the position of the entity and updates the 'last' variables
     */
    public void setPositionAndUpdate(double x, double y, double z)
    {
        this.setLocationAndAngles(x, y, z, this.rotYaw, this.rotPitch);
    }

//    public boolean getAlwaysRenderNameTagForRender()
//    {
//        return this.getAlwaysRenderNameTag();
//    }

    public void onDataWatcherUpdate(int dataID)
    {
    }

    public Facing getHorizontalFacing()
    {
        return Facing.getHorizontal(ExtMath.floord((double)(this.rotYaw * 4.0F / 360.0F) + 0.5D) & 3);
    }

    public boolean isVisibleTo(EntityNPC player)
    {
        return true;
    }

    public BoundingBox getEntityBoundingBox()
    {
        return this.bbox;
    }

    public void setEntityBoundingBox(BoundingBox bb)
    {
        this.bbox = bb;
    }

    public float getEyeHeight()
    {
        return this.height * 0.85F;
    }

    public boolean isOutsideBorder()
    {
        return this.outsideBorder;
    }

    public void setOutsideBorder(boolean outsideBorder)
    {
        this.outsideBorder = outsideBorder;
    }

//    public boolean setSlot(int slot, ItemStack stack)
//    {
//        return false;
//    }

    /**
     * Get the position in the world. <b>{@code null} is not allowed!</b> If you are not an entity in the world, return
     * the coordinates 0, 0, 0
     */
    public final BlockPos getPosition()
    {
        return new BlockPos(this.posX, this.posY + 0.5D, this.posZ);
    }

    public TagObject getTag()
    {
        return null;
    }

    /**
     * Called when client receives entity's tags from server.
     */
    public void clientUpdateEntityTags(TagObject compound)
    {
    }

//    /**
//     * New version of interactWith that includes vector information on where precisely the player targeted.
//     */
//    public boolean interactAt(EntityNPC player, Vec3 targetVec3)
//    {
//        return false;
//    }

    public boolean isImmuneToExplosions()
    {
        return false;
    }

    protected void applyEnchantments(EntityLiving entityLivingBaseIn, Entity entityIn)
    {
        if (entityIn instanceof EntityLiving)
        {
            EnchantmentHelper.applyThornEnchantments((EntityLiving)entityIn, entityLivingBaseIn);
        }

        EnchantmentHelper.applyArthropodEnchantments(entityLivingBaseIn, entityIn);
    }

    public abstract int getTrackingRange();
    public abstract int getUpdateFrequency();
    public abstract boolean isSendingVeloUpdates();
    
    public boolean isMagnetic() {
    	return false;
    }
    
	public boolean hasSpawnVelocity() {
		return false;
	}
	
	public boolean isPlayer() {
        return false;
    }
	
    public Item getItem() {
    	String id = EntityRegistry.getEntityString(this);
        if(!EntityRegistry.SPAWN_EGGS.containsKey(id))
            return null;
        return ItemRegistry.getRegisteredItem(id.toLowerCase() + "_spawner");
    }
    
    public Position getPos() {
    	return new Position(this.posX, this.posY, this.posZ, this.rotYaw, this.rotPitch, this.worldObj.dimension.getDimensionId());
    }
    
    public abstract EntityType getType();
}
