package common.entity.types;

import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;

import common.ai.EntityAIBase;
import common.ai.EntityAIMoveTowardsRestriction;
import common.ai.EntityAITasks;
import common.ai.EntityJumpHelper;
import common.ai.EntityLookHelper;
import common.ai.EntityMoveHelper;
import common.ai.EntitySenses;
import common.attributes.Attribute;
import common.attributes.AttributeMap;
import common.block.Block;
import common.block.SoundType;
import common.collect.Lists;
import common.collect.Maps;
import common.color.TextColor;
import common.enchantment.EnchantmentHelper;
import common.entity.DamageSource;
import common.entity.Entity;
import common.entity.EntityDamageSource;
import common.entity.animal.EntityWolf;
import common.entity.item.EntityItem;
import common.entity.item.EntityLeashKnot;
import common.entity.item.EntityXp;
import common.entity.npc.Alignment;
import common.entity.npc.Energy;
import common.entity.npc.EntityNPC;
import common.entity.projectile.EntityArrow;
import common.init.BlockRegistry;
import common.init.Blocks;
import common.init.EntityRegistry;
import common.init.ItemRegistry;
import common.init.Items;
import common.init.SoundEvent;
import common.item.Item;
import common.item.ItemStack;
import common.item.spawner.ItemMonsterPlacer;
import common.item.tool.ItemArmor;
import common.model.ParticleType;
import common.network.IPlayer;
import common.packet.SPacketEntityAttach;
import common.packet.SPacketAnimation;
import common.packet.SPacketCollectItem;
import common.pathfinding.PathNavigate;
import common.pathfinding.PathNavigateGround;
import common.potion.Potion;
import common.potion.PotionEffect;
import common.potion.PotionHelper;
import common.rng.Random;
import common.tags.TagObject;
import common.util.BlockPos;
import common.util.BoundingBox;
import common.util.ExtMath;
import common.util.Vec3;
import common.vars.Vars;
import common.world.State;
import common.world.World;
import common.world.AWorldServer;

public abstract class EntityLiving extends Entity
{
    private static final ItemStack[] EMPTY_INV = new ItemStack[5];
    
    protected AttributeMap attributes = new AttributeMap();
	private final List<CombatEntry> combat = Lists.<CombatEntry>newArrayList();
    private final Map<Potion, PotionEffect> effects = Maps.<Potion, PotionEffect>newEnumMap(Potion.class);
	public int soundTimer;
	protected int xpValue;
	private EntityLookHelper lookHelper;
	protected EntityMoveHelper moveHelper;
	protected EntityJumpHelper jumpHelper;
	private EntityBodyHelper bodyHelper;
	protected PathNavigate navigator;
	protected final EntityAITasks tasks;
	protected final EntityAITasks targets;
	private EntityLiving target;
	private EntitySenses senses;
	private boolean leashed;
	private Entity leashedTo;
	private TagObject leashTag;

    public float prevCamPitch;
    public float camPitch;
    public float yawOffset;
    public float prevYawOffset;
    public float headYaw;
    public float prevHeadYaw;
    public float jumpMovement = 0.02F;
    protected float landMovement;
    protected float prevGroundFactor;
    protected float groundFactor;
    protected float movedDist;
    protected float prevMovedDist;
    protected boolean jumping;
    public float moveStrafe;
    public float moveForward;
    protected float randomYawVelo;
    protected int moveIncrements;
    protected double newX;
    protected double newY;
    protected double newZ;
    protected double newYaw;
    protected double newPitch;
    private int jumpTimer;
    
    public boolean swingingItem;
    public int swingTimer;
    public float prevSwingFactor;
    public float swingFactor;
    public float prevLswingAmount;
    public float lswingAmount;
    public float limbSwing;
    
    public int arrowTimer;
    public int hurtTime;
    public float attackedYaw;
    public int maxHurtTime;
    public int deathTime;
    public int hurtCooldown = 20;
    protected int lastDamage;
    protected EntityNPC playerAttacker;
    protected int recentlyHit;
    private EntityLiving attackedBy;
    private int lastAttacked;
    private EntityLiving attacker;
    private int lastAttackTime;
	private int lastDamaged;
    
//    protected int entityAge;
    private int absorptionAmount;
    private boolean effectsDirty = true;
    protected boolean firstEffectUpdate = true;
	private boolean attacked;
	private boolean damaged;
	
	private String description;

	private String blockType;
    
    private float radiation;

    protected int particleTimer;
    private BlockPos homePosition = BlockPos.ORIGIN;
    private float maximumHomeDistance = -1.0F;
    protected int growingAge;
    private float ageWidth = -1.0F;
    private float ageHeight;
    private EntityAIBase aiBase = new EntityAIMoveTowardsRestriction(this, 1.0D);
    private boolean isMovementAITaskSet;
    private float baseSpeed = 0.7f;
    private float modSpeed = 1.0f;

    public EntityLiving(World worldIn)
    {
        super(worldIn);
        this.applyEntityAttributes();
        this.setHealth(this.getMaxHealth());
        this.preventSpawning = true;
//        this.randomUnused1 = (float)((Math.random() + 1.0D) * 0.009999999776482582D);
        this.setPosition(this.posX, this.posY, this.posZ);
//        this.randomUnused2 = (float)Math.random() * 12398.0F;
        this.rotYaw = (float)(Math.random() * Math.PI * 2.0D);
        this.headYaw = this.rotYaw;
        this.stepHeight = 0.6F;

		this.tasks = new EntityAITasks();
		this.targets = new EntityAITasks();
		this.lookHelper = new EntityLookHelper(this);
		this.moveHelper = new EntityMoveHelper(this);
		this.jumpHelper = new EntityJumpHelper(this);
		this.bodyHelper = new EntityBodyHelper(this);
		this.navigator = this.getNewNavigator(worldIn);
		this.senses = new EntitySenses(this);
    }

    protected void entityInit()
    {
        this.dataWatcher.addObject(2, 0); // potion color
        this.dataWatcher.addObject(3, (byte)0); // potion ambient
        this.dataWatcher.addObject(4, (byte)0); // arrows
        this.dataWatcher.addObject(5, 1); // max health
        this.dataWatcher.addObject(6, 1); // health
        this.dataWatcher.addObject(7, 0); // age
    }

    protected void applyEntityAttributes()
    {
    }

    protected void updateFallState(double y, boolean onGroundIn, Block blockIn, BlockPos pos)
    {
        if (!this.isInLiquid())
        {
            this.handleWaterMovement();
        }

        if (!this.worldObj.client && this.fallDistance > 3.0F && onGroundIn)
        {
            this.makeLandingParticles(pos);
        }

        super.updateFallState(y, onGroundIn, blockIn, pos);
    }

    public void makeLandingParticles(BlockPos pos)
    {
    	State iblockstate = this.worldObj.getState(pos);
        Block block = iblockstate.getBlock();
        float f = (float)ExtMath.ceilf(this.fallDistance - 3.0F);

        if (block != Blocks.air)
        {
            double d0 = (double)Math.min(0.2F + f / 15.0F, 10.0F);

            if (d0 > 2.5D)
            {
                d0 = 2.5D;
            }

            int i = (int)(150.0D * d0);
            ((AWorldServer)this.worldObj).spawnParticle(ParticleType.BLOCK_DUST, this.posX, this.posY, this.posZ, i, 0.0D, 0.0D, 0.0D, 0.15000000596046448D, BlockRegistry.getStateId(iblockstate));
        }
    }

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

    /**
     * Gets called every tick from main Entity class
     */
    public void onEntityUpdate()
    {
        this.prevSwingFactor = this.swingFactor;
        super.onEntityUpdate();
//        this.worldObj.profiler.start("livingEntityBaseTick");
        boolean flag = this.isPlayer();

        if (this.isEntityAlive())
        {
            if ((this.worldObj.client || Vars.damageWall) && this.isEntityInsideOpaqueBlock())
            {
                this.attackEntityFrom(DamageSource.inWall, 1);
            }
//            else if ((this.worldObj.client || Config.damageBorder) && flag && !this.worldObj.getWorldBorder().contains(this.getEntityBoundingBox()))
//            {
//                double d0 = this.worldObj.getWorldBorder().getClosestDistance(this) + this.worldObj.getWorldBorder().getDamageBuffer();
//
//                if (d0 < 0.0D)
//                {
//                    this.attackEntityFrom(DamageSource.inWall, (float)Math.max(1, MathHelper.floor_double(-d0 * this.worldObj.getWorldBorder().getDamageAmount())));
//                }
//            }
        }

        if (this.isImmuneToFire() || this.worldObj.client)
        {
            this.extinguish();
        }

//        boolean flag1 = flag && ((EntityNPC)this).capabilities.disableDamage;

//        if (this.isEntityAlive())
//        {
//            if (this.isInsideOfLiquid())
//            {
//                if (!this.canBreatheUnderwater() && !this.hasEffect(Potion.waterBreathing.id) && !flag1)
//                {
//                    this.setAir(this.decreaseAirSupply(this.getAir()));
//
//                    if (this.getAir() == -20)
//                    {
//                        this.setAir(0);
//                        
//                        if(this.worldObj.client || Config.damageDrown) {
//	                        for (int i = 0; i < 8; ++i)
//	                        {
//	                            float f = this.rand.floatv() - this.rand.floatv();
//	                            float f1 = this.rand.floatv() - this.rand.floatv();
//	                            float f2 = this.rand.floatv() - this.rand.floatv();
//	                            this.worldObj.spawnParticle(EnumParticleTypes.WATER_BUBBLE, this.posX + (double)f, this.posY + (double)f1, this.posZ + (double)f2, this.motionX, this.motionY, this.motionZ);
//	                        }
//	
//	                        this.attackEntityFrom(DamageSource.drown, 2);
//                        }
//                    }
//                }
//
//                if (!this.worldObj.client && this.isRiding() && this.vehicle instanceof EntityLivingBase)
//                {
//                    this.mountEntity((Entity)null);
//                }
//            }
//            else
//            {
//                this.setAir(300);
//            }
//        }

        if (this.isEntityAlive() && this.isWet())
        {
            this.extinguish();
        }

        this.prevCamPitch = this.camPitch;

        if (this.hurtTime > 0)
        {
            --this.hurtTime;
        }

        if (this.hurtResistance > 0 && (this.worldObj.client || !(this.isPlayer())))
        {
            --this.hurtResistance;
        }

        if (this.getHealth() <= 0)
        {
            this.onDeathUpdate();
        }

        if (this.recentlyHit > 0)
        {
            --this.recentlyHit;
        }
        else
        {
            this.playerAttacker = null;
        }

        if (this.attacker != null && !this.attacker.isEntityAlive())
        {
            this.attacker = null;
        }
        
//        if(this.attackedBy.isPlayer() && ((EntityNPC)this.attackedBy).capabilities.isCreativeMode) {
//        	this.attackedBy = null; // FIX Creative
//        }

        if (this.attackedBy != null)
        {
            if (!this.attackedBy.isEntityAlive())
            {
                this.setAttackedBy(null);
            }
            else if (this.ticksExisted - this.lastAttacked > 100)
            {
                this.setAttackedBy(null);
            }
        }

        if(!this.worldObj.client) {
        	if(!this.firstEffectUpdate && Vars.radiation) { // && 
        			// (!(this.isPlayer()) || !((EntityNPCMP)this).creative)) {
		        float radiation = this.radiation + this.attributes.get(Attribute.RADIATION);
		        radiation -= 10.0f + this.attributes.get(Attribute.RADIATION_RESISTANCE);
	//	        if(this.isPlayer())
	//	        	Log.SERVER.info("rad:" + radiation);
		        if(radiation >= 0.0f) {
		        	this.addEffect(new PotionEffect(Potion.RADIATION, ExtMath.clampi((int)(radiation * 5.0f), 10, 32766), 
		        			ExtMath.clampi((int)(radiation / 20.0f), 0, 255)));
		        }
        	}
        	else {
        		this.firstEffectUpdate = false;
        	}
//        	if(this.isPlayer() && ((EntityNPCMP)this).creative) {
//        		this.radiation = 0.0f;
//        		if(this.hasEffect(Potion.radiation.id))
//        			this.removeEffect(Potion.radiation.id);
//        	}
//        	else
        	if(this.radiation > 0.0f) {
        		this.radiation *= 0.95f;
        	}
        	if(this.radiation < 0.005f)
    			this.radiation = 0.0f;
        }
        this.updateEffects();
        this.prevMovedDist = this.movedDist;
        this.prevYawOffset = this.yawOffset;
        this.prevHeadYaw = this.headYaw;
        this.prevYaw = this.rotYaw;
        this.prevPitch = this.rotPitch;
//        this.worldObj.profiler.end();

		if(this.isEntityAlive() && this.rand.zrange(1000) < this.soundTimer++) {
			this.soundTimer = -this.getTalkInterval();
			this.playLivingSound();
		}

//		if(!(this.isPlayer()) && 
//				this.target.isPlayer() && ((EntityNPC)this.target).creative) {
//			this.target = null; // FIX Creative
//		}
    }

//    /**
//     * If Animal, checks if the age timer is negative
//     */
//    public boolean isChild()
//    {
//        return false;
//    }

    /**
     * handles entity death timer, experience orb and particle creation
     */
    protected void onDeathUpdate()
    {
        ++this.deathTime;

        if (this.deathTime == 20)
        {
            if (!this.worldObj.client && (this.recentlyHit > 0 || this.isPlayer()) && this.canDropLoot() && Vars.mobXP)
            {
                int i = this.getExperiencePoints(this.playerAttacker);

                while (i > 0)
                {
                    int j = EntityXp.getXPSplit(i);
                    i -= j;
                    this.worldObj.spawnEntityInWorld(new EntityXp(this.worldObj, this.posX, this.posY, this.posZ, j));
                }
            }

            this.setDead();

            for (int k = 0; k < 20; ++k)
            {
                double d2 = this.rand.gaussian() * 0.02D;
                double d0 = this.rand.gaussian() * 0.02D;
                double d1 = this.rand.gaussian() * 0.02D;
                this.worldObj.spawnParticle(ParticleType.EXPLOSION_NORMAL, this.posX + (double)(this.rand.floatv() * this.width * 2.0F) - (double)this.width, this.posY + (double)(this.rand.floatv() * this.height), this.posZ + (double)(this.rand.floatv() * this.width * 2.0F) - (double)this.width, d2, d0, d1);
            }
        }
    }

    /**
     * Entity won't drop items or experience points if this returns false
     */
    protected boolean canDropLoot()
    {
        return true;
    }
    
//    protected int decreaseAirSupply(int air)
//    {
////        int i = EnchantmentHelper.getRespiration(this);
//        return /* i > 0 && this.rand.rarity(i + 1) ? air : */ air - 1;
//    }

//    /**
//     * Get the experience points the entity currently has.
//     */
//    protected int getExperiencePoints(EntityNPC player)
//    {
//        return 0;
//    }

//    /**
//     * Only use is to identify if class is an instance of player for experience dropping
//     */
    
    public String getDescription() {
    	return this.description;
    }
    
    public void setDescription(String desc) {
    	this.description = desc;
    }

    public Random getRNG()
    {
        return this.rand;
    }

    public EntityLiving getAttackedBy()
    {
        return this.attackedBy;
    }

    public int getAttackedTime()
    {
        return this.lastAttacked;
    }

    public void setAttackedBy(EntityLiving livingBase)
    {
        this.attackedBy = livingBase;
        this.lastAttacked = this.ticksExisted;
    }

    public EntityLiving getLastAttack()
    {
        return this.attacker;
    }

    public int getLastAttackTime()
    {
        return this.lastAttackTime;
    }

    public void setLastAttack(Entity entity)
    {
        if (entity instanceof EntityLiving)
        {
            this.attacker = (EntityLiving)entity;
        }
        else
        {
            this.attacker = null;
        }

        this.lastAttackTime = this.ticksExisted;
    }

//    public int getAge()
//    {
//        return this.entityAge;
//    }

    /**
     * (abstract) Protected helper method to write subclass entity data to NBT.
     */
    public void writeEntity(TagObject tagCompound)
    {
    	tagCompound.setInt("MaxHealth", this.getMaxHealth());
        tagCompound.setInt("Health", this.getHealth());
//        tagCompound.setShort("Health", (short)((int)Math.ceil((double)this.getHealth())));
        tagCompound.setShort("HurtTime", (short)this.hurtTime);
        tagCompound.setInt("HurtByTimestamp", this.lastAttacked);
        tagCompound.setFloat("Radiation", this.radiation);
        tagCompound.setShort("DeathTime", (short)this.deathTime);
        tagCompound.setInt("Absorption", this.getAbsorptionAmount());
        tagCompound.setFloat("SpeedBase", this.getSpeedBase());

        if (!this.effects.isEmpty())
        {
        	List<TagObject> nbttaglist = Lists.newArrayList();

            for (PotionEffect potioneffect : this.effects.values())
            {
                nbttaglist.add(potioneffect.toTags());
            }

            tagCompound.setList("ActiveEffects", nbttaglist);
        }

		tagCompound.setBool("Leashed", this.leashed);

		if(this.leashedTo != null) {
			TagObject nbttagcompound1 = new TagObject();

//			if(this.leashedTo.isPlayer()) {
//				nbttagcompound1.setString("PlayerName", ((EntityNPC)this.leashedTo).getUser());
//			}
//			else
			if(this.leashedTo instanceof EntityLeashKnot) {
				BlockPos blockpos = ((EntityLeashKnot)this.leashedTo).getHangingPosition();
				nbttagcompound1.setInt("X", blockpos.getX());
				nbttagcompound1.setInt("Y", blockpos.getY());
				nbttagcompound1.setInt("Z", blockpos.getZ());
			}

			tagCompound.setObject("Leash", nbttagcompound1);
		}

//		if(this.isAIDisabled()) {
//			tagCompound.setBoolean("NoAI", this.isAIDisabled());
//		}
        tagCompound.setInt("Age", this.getGrowingAge());
        if(this.description != null)
        	tagCompound.setString("Description", this.description);
    }

    /**
     * (abstract) Protected helper method to read subclass entity data from NBT.
     */
    public void readEntity(TagObject tagCompund)
    {
        this.setAbsorptionAmount(tagCompund.getInt("Absorption"));
        this.setSpeedBase(tagCompund.getFloat("SpeedBase"));

        if (tagCompund.hasList("ActiveEffects"))
        {
        	List<TagObject> nbttaglist = tagCompund.getList("ActiveEffects");

            for (int i = 0; i < nbttaglist.size(); ++i)
            {
                TagObject nbttagcompound = nbttaglist.get(i);
                PotionEffect potioneffect = PotionEffect.fromTags(nbttagcompound);

                if (potioneffect != null && !potioneffect.getPotion().isInstant())
                {
                    this.effects.put(potioneffect.getPotion(), potioneffect);
                }
            }
        }

        this.setMaxHealth(Math.max(1, tagCompund.getInt("MaxHealth")));
        if (tagCompund.hasInt("Health"))
        {
            this.setHealth(tagCompund.getInt("Health"));
        }
        else
        {
//            NBTBase nbtbase = tagCompund.getTag("Health");
//
//            if (nbtbase == null)
//            {
            this.setHealth(this.getMaxHealth());
//            }
//            else if (nbtbase.getId() == 5)
//            {
//                this.setHealth((int)((NBTTagFloat)nbtbase).getFloat());
//            }
//            else if (nbtbase.getId() == 2)
//            {
//                this.setHealth((int)((NBTTagShort)nbtbase).getShort());
//            }
        }

        this.hurtTime = tagCompund.getShort("HurtTime");
        this.deathTime = tagCompund.getShort("DeathTime");
        this.lastAttacked = tagCompund.getInt("HurtByTimestamp");
        this.radiation = tagCompund.getFloat("Radiation");

        if(!(this.isPlayer())) {
			this.leashed = tagCompund.getBool("Leashed");
	
			if(this.leashed && tagCompund.hasObject("Leash")) {
				this.leashTag = tagCompund.getObject("Leash");
			}
        }

//		this.setNoAI(tagCompund.getBoolean("NoAI"));
        this.setGrowingAge(tagCompund.getInt("Age"));
		this.description = tagCompund.hasString("Description") ? tagCompund.getString("Description") : null;
		if(this.description != null && this.description.isEmpty())
			this.description = null;
    }

    protected void updateEffects()
    {
        Iterator<Potion> iterator = this.effects.keySet().iterator();

        while (iterator.hasNext())
        {
        	Potion potion = iterator.next();
            PotionEffect potioneffect = this.effects.get(potion);

            if (!potioneffect.onUpdate(this))
            {
                if (!this.worldObj.client)
                {
                    iterator.remove();
                    this.onFinishedEffect(potioneffect);
                }
            }
            else if (potioneffect.getRemaining() % 600 == 0)
            {
                this.onChangedEffect(potioneffect, false);
            }
        }

        if (this.effectsDirty)
        {
            if (!this.worldObj.client)
            {
                this.updateEffectMeta();
            }

            this.effectsDirty = false;
        }

        int i = this.dataWatcher.getWatchableObjectInt(2);
        boolean flag1 = this.dataWatcher.getWatchableObjectByte(3) > 0;

        if (i > 0)
        {
            boolean flag = false;

//            if (!this.isInvisible())
//            {
            flag = this.rand.chance();
//            }
//            else
//            {
//                flag = this.rand.chance(15);
//            }

            if (flag1)
            {
                flag &= this.rand.chance(5);
            }

            if (flag && i > 0)
            {
                double d0 = (double)(i >> 16 & 255) / 255.0D;
                double d1 = (double)(i >> 8 & 255) / 255.0D;
                double d2 = (double)(i >> 0 & 255) / 255.0D;
                this.worldObj.spawnParticle(flag1 ? ParticleType.SPELL_MOB_AMBIENT : ParticleType.SPELL_MOB, this.posX + (this.rand.doublev() - 0.5D) * (double)this.width, this.posY + this.rand.doublev() * (double)this.height, this.posZ + (this.rand.doublev() - 0.5D) * (double)this.width, d0, d1, d2);
            }
        }
    }

    /**
     * Clears potion metadata values if the entity has no potion effects. Otherwise, updates potion effect color,
     * ambience, and invisibility metadata values
     */
    protected void updateEffectMeta()
    {
        if (this.effects.isEmpty())
        {
            this.resetPotionEffectMetadata();
//            this.setInvisible(false);
        }
        else
        {
            int i = PotionHelper.calcPotionLiquidColor(this.effects.values());
            this.dataWatcher.updateObject(3, Byte.valueOf((byte)(PotionHelper.getAreAmbient(this.effects.values()) ? 1 : 0)));
            this.dataWatcher.updateObject(2, Integer.valueOf(i));
//            this.setInvisible(this.hasEffect(Potion.invisibility.id));
        }
    }

    /**
     * Resets the potion effect color and ambience metadata values
     */
    protected void resetPotionEffectMetadata()
    {
        this.dataWatcher.updateObject(3, Byte.valueOf((byte)0));
        this.dataWatcher.updateObject(2, Integer.valueOf(0));
    }

    public void clearEffects(boolean negative)
    {
        Iterator<Potion> iterator = this.effects.keySet().iterator();

        while (iterator.hasNext())
        {
        	Potion potion = iterator.next();
            PotionEffect potioneffect = this.effects.get(potion);

            if (!this.worldObj.client && (!negative || potioneffect.getPotion().isBadEffect()))
            {
                iterator.remove();
                this.onFinishedEffect(potioneffect);
            }
        }
    }

    public Collection<PotionEffect> getEffects()
    {
        return this.effects.values();
    }

//    public boolean hasEffect(int potionId)
//    {
//        return this.effects.containsKey(Integer.valueOf(potionId));
//    }

    public boolean hasEffect(Potion potionIn)
    {
        return this.effects.containsKey(potionIn);
    }

    /**
     * returns the PotionEffect for the supplied Potion if it is active, null otherwise.
     */
    public PotionEffect getEffect(Potion potionIn)
    {
        return this.effects.get(potionIn);
    }

    /**
     * adds a PotionEffect to the entity
     */
    public void addEffect(PotionEffect potioneffectIn)
    {
        if (!potioneffectIn.getPotion().isInstant() && this.isPotionApplicable(potioneffectIn.getPotion(), potioneffectIn.getAmplifier()))
        {
            if (this.effects.containsKey(potioneffectIn.getPotion()))
            {
            	this.effects.put(potioneffectIn.getPotion(), this.effects.get(potioneffectIn.getPotion()).combine(potioneffectIn));
                this.onChangedEffect(this.effects.get(potioneffectIn.getPotion()), true);
            }
            else
            {
                this.effects.put(potioneffectIn.getPotion(), potioneffectIn);
                this.onNewEffect(potioneffectIn);
            }
        }
    }

	public boolean isPotionApplicable(Potion potion, int amplifier) {
		return potion == Potion.SPEED || potion == Potion.SLOWNESS || potion == Potion.HEAL || potion == Potion.DAMAGE || potion == Potion.POISON || potion == Potion.RADIATION;
	}
    
    public boolean arePotionsInverted()
    {
        return false;
    }

    /**
     * Remove the speified potion effect from this entity.
     */
    public void removeEffectClient(Potion potionId)
    {
        this.effects.remove(potionId);
    }

    /**
     * Remove the specified potion effect from this entity.
     */
    public void removeEffect(Potion potion)
    {
        PotionEffect potioneffect = this.effects.remove(potion);

        if (potioneffect != null)
        {
            this.onFinishedEffect(potioneffect);
        }
    }

    protected void onNewEffect(PotionEffect id)
    {
        this.effectsDirty = true;

        if (!this.worldObj.client)
        {
            id.getPotion().addModifiers(this, id.getAmplifier());
        }
    }

    protected void onChangedEffect(PotionEffect id, boolean added)
    {
        this.effectsDirty = true;

        if (added && !this.worldObj.client)
        {
            id.getPotion().removeModifiers(this, id.getAmplifier());
            id.getPotion().addModifiers(this, id.getAmplifier());
        }
    }

    protected void onFinishedEffect(PotionEffect effect)
    {
        this.effectsDirty = true;

        if (!this.worldObj.client)
        {
            effect.getPotion().removeModifiers(this, effect.getAmplifier());
        }
    }

    /**
     * Heal living entity (param: amount of half-hearts)
     */
    public void heal(int healAmount)
    {
        int f = this.getHealth();

        if (f > 0)
        {
            this.setHealth(f + healAmount);
        }
    }

    public final int getHealth()
    {
        return this.dataWatcher.getWatchableObjectInt(6);
    }

    public void setHealth(int health)
    {
        this.dataWatcher.updateObject(6, Integer.valueOf(ExtMath.clampi(health, 0, this.getMaxHealth())));
    }
    
    public boolean isImmuneToFire() {
    	return this.hasEffect(Potion.FIRE_RESISTANCE);
    }

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

            if (this.getHealth() <= 0)
            {
                return false;
            }
            else if (source.isFireDamage() && this.isImmuneToFire()) //  || this.hasEffect(Potion.fireResistance)))
            {
                return false;
            }
            else
            {
                if ((source == DamageSource.anvil || source == DamageSource.fallingBlock) && this.getItem(4) != null)
                {
                    this.getItem(4).damageItem((int)(amount * 4.0F + this.rand.floatv() * (float)amount * 2.0F), this);
                    amount = (int)((float)amount * 0.75F);
                }

                this.lswingAmount = 1.5F;
                boolean flag = true;

                if ((float)this.hurtResistance > (float)this.hurtCooldown / 2.0F && amount < Integer.MAX_VALUE)
                {
                    if (amount <= this.lastDamage)
                    {
                        return false;
                    }

                    this.damageEntity(source, amount - this.lastDamage);
                    this.lastDamage = amount;
                    flag = false;
                }
                else
                {
                    this.lastDamage = amount;
                    this.hurtResistance = this.hurtCooldown = Vars.hurtDelay;
                    this.damageEntity(source, amount);
                    this.hurtTime = this.maxHurtTime = 10;
                }

                this.attackedYaw = 0.0F;
                Entity entity = source.getEntity();

                if (entity != null)
                {
                    if (entity instanceof EntityLiving)
                    {
                        this.setAttackedBy((EntityLiving)entity);
                    }

                    if (entity.isPlayer())
                    {
                        this.recentlyHit = 100;
                        this.playerAttacker = (EntityNPC)entity;
                    }
                    else if (entity instanceof EntityWolf)
                    {
                        EntityWolf entitywolf = (EntityWolf)entity;

                        if (entitywolf.isTamed())
                        {
                            this.recentlyHit = 100;
                            this.playerAttacker = null;
                        }
                    }
                }

                if (flag)
                {
                    this.worldObj.setEntityState(this, (byte)2);

//                    if (source != DamageSource.drown)
//                    {
                    this.setBeenAttacked();
//                    }

                    if (entity != null)
                    {
                        double d1 = entity.posX - this.posX;
                        double d0;

                        for (d0 = entity.posZ - this.posZ; d1 * d1 + d0 * d0 < 1.0E-4D; d0 = (Math.random() - Math.random()) * 0.01D)
                        {
                            d1 = (Math.random() - Math.random()) * 0.01D;
                        }

                        this.attackedYaw = (float)(ExtMath.atan2(d0, d1) * 180.0D / Math.PI - (double)this.rotYaw);
                        this.knockBack(entity, amount, d1, d0);
                    }
                    else
                    {
                        this.attackedYaw = (float)((int)(Math.random() * 2.0D) * 180);
                    }
                }

                if (this.getHealth() <= 0)
                {
                	SoundEvent s = this.getDeathSound();

                    if (flag && s != null)
                    {
                        this.playSound(s, this.getSoundVolume());
                    }

                    this.onDeath(source);
                }
                else
                {
                	SoundEvent s1 = this.getHurtSound();

                    if (flag && s1 != null)
                    {
                        this.playSound(s1, this.getSoundVolume());
                    }
                }

                return true;
            }
        }
    }

    /**
     * Renders broken item particles using the given ItemStack
     */
    public void renderBrokenItemStack(ItemStack stack)
    {
        this.playSound(SoundEvent.BREAK, 0.8F);

        for (int i = 0; i < 5; ++i)
        {
            Vec3 vec3 = new Vec3(((double)this.rand.floatv() - 0.5D) * 0.1D, Math.random() * 0.1D + 0.1D, 0.0D);
            vec3 = vec3.rotatePitch(-this.rotPitch * (float)Math.PI / 180.0F);
            vec3 = vec3.rotateYaw(-this.rotYaw * (float)Math.PI / 180.0F);
            double d0 = (double)(-this.rand.floatv()) * 0.6D - 0.3D;
            Vec3 vec31 = new Vec3(((double)this.rand.floatv() - 0.5D) * 0.3D, d0, 0.6D);
            vec31 = vec31.rotatePitch(-this.rotPitch * (float)Math.PI / 180.0F);
            vec31 = vec31.rotateYaw(-this.rotYaw * (float)Math.PI / 180.0F);
            vec31 = vec31.addVector(this.posX, this.posY + (double)this.getEyeHeight(), this.posZ);
            this.worldObj.spawnParticle(ParticleType.ITEM_CRACK, vec31.xCoord, vec31.yCoord, vec31.zCoord, vec3.xCoord, vec3.yCoord + 0.05D, vec3.zCoord, ItemRegistry.getIdFromItem(stack.getItem()));
        }
    }

    /**
     * Called when the mob's health reaches 0.
     */
    public void onDeath(DamageSource cause)
    {
		if(!this.worldObj.client && !(this.isPlayer())) {
	        EntityLiving entity = this.getAttackingEntity();
	        if(entity != null && entity.isPlayer())
	        	((EntityNPC)entity).connection.addToPlayerScore(this);
		}
        Entity entity = cause.getEntity();

        if (entity != null)
        {
            entity.onKillEntity(this);
        }
        
        if(!this.worldObj.client)
        	this.sendDeathMessage();

//        this.noPickup = true;
        this.resetCombat();

        if (!this.worldObj.client)
        {
            int i = 0;

            if (entity != null && entity.isPlayer())
            {
                i = EnchantmentHelper.getLootingModifier((EntityLiving)entity);
            }

            if (this.canDropLoot() && Vars.dropLoot)
            {
                this.dropFewItems(this.recentlyHit > 0, i);
//                this.dropEquipment(this.recentlyHit > 0, i);

                if (this.recentlyHit > 0 && this.rand.floatv() < 0.025F + (float)i * 0.01F)
                {
                    this.addRandomDrop();
                }
            }
        }

        this.worldObj.setEntityState(this, (byte)3);
    }

//    /**
//     * Drop the equipment for this entity.
//     *  
//     * @param wasRecentlyHit true if this this entity was recently hit by appropriate entity (generally only if player
//     * or tameable)
//     * @param lootingModifier level of enchanment to be applied to this drop
//     */
//    protected void dropEquipment(boolean wasRecentlyHit, int lootingModifier)
//    {
//    }

    /**
     * knocks back this entity
     */
    public void knockBack(Entity source, float damage, double xfactor, double zfactor)
    {
        if (!this.hasEffect(Potion.STABILITY) && this.rand.doublev() >= this.getKnockBackResistance())
        {
            this.isAirBorne = true;
            float div = ExtMath.sqrtd(xfactor * xfactor + zfactor * zfactor);
            float mult = 0.4F * Vars.knockback;
            this.motionX /= 2.0D;
            this.motionY /= 2.0D;
            this.motionZ /= 2.0D;
            this.motionX -= xfactor / (double)div * (double)mult;
            this.motionY += (double)mult;
            this.motionZ -= zfactor / (double)div * (double)mult;

            if (this.motionY > 0.4000000059604645D)
            {
                this.motionY = 0.4000000059604645D;
            }
        }
    }

    /**
     * Returns the sound this mob makes when it is hurt.
     */
    protected SoundEvent getHurtSound()
    {
        return SoundEvent.HIT;
    }

    /**
     * Returns the sound this mob makes on death.
     */
    protected SoundEvent getDeathSound()
    {
        return SoundEvent.HIT;
    }

    /**
     * Causes this Entity to drop a random item.
     */
    protected void addRandomDrop()
    {
    }

//    /**
//     * Drop 0-2 items of this living's type
//     *  
//     * @param wasRecentlyHit true if this this entity was recently hit by appropriate entity (generally only if player
//     * or tameable)
//     * @param lootingModifier level of enchanment to be applied to this drop
//     */
//    protected void dropFewItems(boolean wasRecentlyHit, int lootingModifier)
//    {
//    }

    /**
     * returns true if this entity is by a ladder, false otherwise
     */
    public boolean isOnLadder()
    {
        int i = ExtMath.floord(this.posX);
        int j = ExtMath.floord(this.getEntityBoundingBox().minY);
        int k = ExtMath.floord(this.posZ);
        Block block = this.worldObj.getState(new BlockPos(i, j, k)).getBlock();
        return (block == Blocks.ladder || block == Blocks.vine); // && (!(this.isPlayer()) || !((EntityNPC)this).isSpectator());
    }

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

    public void fall(float distance, float damageMultiplier)
    {
    	damageMultiplier = Math.max(0.0f, damageMultiplier - (1.65f - (float)this.worldObj.gravity * 1.65f));
        super.fall(distance, damageMultiplier);
        PotionEffect potioneffect = this.getEffect(Potion.JUMP);
        float f = potioneffect != null ? (float)(potioneffect.getAmplifier() + 1) : 0.0F;
        int i = ExtMath.ceilf((distance - 3.0F - f) * damageMultiplier);

        if (i > 0)
        {
        	if(Vars.damageFall) {
	            this.playSound(i > 4 ? SoundEvent.FALL_BIG : SoundEvent.FALL_SMALL, 1.0F);
	            this.attackEntityFrom(DamageSource.fall, i);
        	}
            int j = ExtMath.floord(this.posX);
            int k = ExtMath.floord(this.posY - 0.20000000298023224D);
            int l = ExtMath.floord(this.posZ);
            Block block = this.worldObj.getState(new BlockPos(j, k, l)).getBlock();

            if (block != Blocks.air)
            {
                SoundType block$soundtype = block.sound;
                if(block$soundtype.getStepSound() != null)
                	this.playSound(block$soundtype.getStepSound(), 0.5F);
            }
        }
    }

//    protected String getFallSoundString(int damageValue)
//    {
//        return ;
//    }

    /**
     * Setups the entity to do the hurt animation. Only used by packets in multiplayer.
     */
    public void performHurtAnimation()
    {
        this.hurtTime = this.maxHurtTime = 10;
        this.attackedYaw = 0.0F;
    }

    /**
     * Returns the current armor value as determined by a call to InventoryPlayer.getTotalArmorValue
     */
    public int getTotalArmorValue()
    {
        int i = 0;

        for (ItemStack itemstack : this.getInventory())
        {
            if (itemstack != null && itemstack.getItem() instanceof ItemArmor)
            {
                int j = ((ItemArmor)itemstack.getItem()).damageReduceAmount;
                i += j;
            }
        }

        return i;
    }

    protected void damageArmor(int p_70675_1_)
    {
    }

    /**
     * Reduces damage, depending on armor
     */
    protected int applyArmorCalculations(DamageSource source, int damage)
    {
        if (!source.isUnblockable())
        {
            this.damageArmor(damage);
        	int armor = this.getTotalArmorValue();
        	if(armor <= 20) {
	            damage = (int)(((float)damage * (float)(25 - armor)) / 25.0F);
        	}
        	else {
        		damage = (int)((float)damage / (5.0 + ((float)(armor - 20)) * 0.2)); //TODO: fix!!!!!!!
        	}
        }

        return damage;
    }

    /**
     * Reduces damage, depending on potions
     */
    protected int applyPotionDamageCalculations(DamageSource source, int damage)
    {
        if (this.hasEffect(Potion.RESISTANCE) && source != DamageSource.outOfWorld)
        {
            int i = (this.getEffect(Potion.RESISTANCE).getAmplifier() + 1) * 5;
            int j = 25 - i;
            float f = (float)damage * (float)j;
            damage = (int)(f / 25.0F);
        }

        if (damage <= 0)
        {
            return 0;
        }
        else
        {
            int k = EnchantmentHelper.getEnchantmentModifierDamage(this.getInventory(), source);

            if (k > 20)
            {
                k = 20;
            }

            if (k > 0 && k <= 20)
            {
                int l = 25 - k;
                float f1 = (float)damage * (float)l;
                damage = (int)(f1 / 25.0F);
            }

            return damage;
        }
    }

    /**
     * Deals damage to the entity. If its a EntityNPC then will take damage from the armor first and then health
     * second with the reduced value. Args: damageAmount
     */
    protected void damageEntity(DamageSource damageSrc, int damageAmount)
    {
//        if (!this.isEntityInvulnerable(damageSrc))
//        {
        damageAmount = this.applyArmorCalculations(damageSrc, damageAmount);
        damageAmount = this.applyPotionDamageCalculations(damageSrc, damageAmount);
        int f = damageAmount;
        damageAmount = Math.max(damageAmount - this.getAbsorptionAmount(), 0);
        this.setAbsorptionAmount(this.getAbsorptionAmount() - (f - damageAmount));

        if (damageAmount != 0)
        {
//                float f1 = this.getHealth();
            this.setHealth(this.getHealth() - damageAmount);
            this.trackDamage(damageSrc, damageAmount);
            this.setAbsorptionAmount(this.getAbsorptionAmount() - damageAmount);
        }
//        }
    }

    public EntityLiving getAttackingEntity()
    {
        return (EntityLiving)(this.getAttacking() != null ? this.getAttacking() : (this.playerAttacker != null ? this.playerAttacker : (this.attackedBy != null ? this.attackedBy : null)));
    }

    public final int getMaxHealth()
    {
        return this.dataWatcher.getWatchableObjectInt(5);
    }

    public final void setMaxHealth(int max)
    {
        this.dataWatcher.updateObject(5, max);
    }

    /**
     * counts the amount of arrows stuck in the entity. getting hit by arrows increases this, used in rendering
     */
    public final int getArrowCountInEntity()
    {
        return this.dataWatcher.getWatchableObjectByte(4);
    }

    /**
     * sets the amount of arrows stuck in the entity. used for rendering those
     */
    public final void setArrowCountInEntity(int count)
    {
        this.dataWatcher.updateObject(4, Byte.valueOf((byte)count));
    }

    /**
     * Returns an integer indicating the end point of the swing animation, used by {@link #swingFactor} to provide a
     * progress indicator. Takes dig speed enchantments into account.
     */
    private int getArmSwingAnimationEnd()
    {
        return 6; // this.hasEffect(Potion.digSpeed) ? 6 - (1 + this.getEffect(Potion.digSpeed).getAmplifier()) * 1 : (this.hasEffect(Potion.digSlowdown) ? 6 + (1 + this.getEffect(Potion.digSlowdown).getAmplifier()) * 2 : 6);
    }

    /**
     * Swings the item the player is holding.
     */
    public void swingItem()
    {
        if (!this.swingingItem || this.swingTimer >= this.getArmSwingAnimationEnd() / 2 || this.swingTimer < 0)
        {
            this.swingTimer = -1;
            this.swingingItem = true;

            if (this.worldObj instanceof AWorldServer)
            {
                ((AWorldServer)this.worldObj).sendToAllTrackingEntity(this, new SPacketAnimation(this, 0));
            }
        }
    }

    public void handleStatusUpdate(byte id)
    {
        if (id == 2)
        {
            this.lswingAmount = 1.5F;
            this.hurtResistance = this.hurtCooldown;
            this.hurtTime = this.maxHurtTime = 10;
            this.attackedYaw = 0.0F;
            SoundEvent s = this.getHurtSound();

            if (s != null)
            {
                this.playSound(s, this.getSoundVolume());
            }

            this.attackEntityFrom(DamageSource.generic, 0);
        }
        else if (id == 3)
        {
        	SoundEvent s1 = this.getDeathSound();

            if (s1 != null)
            {
                this.playSound(s1, this.getSoundVolume());
            }

            this.setHealth(0);
            this.onDeath(DamageSource.generic);
        }
        else if(id == 20) {
			this.spawnExplosionParticle();
		}
        else if(id == 55) {
			if(this.particleTimer == 0)
				this.particleTimer = 40;
		}
        else
        {
            super.handleStatusUpdate(id);
        }
    }
    
    public void kill()
    {
    	if(this.worldObj.client || Vars.damageVoid)
        	this.attackEntityFrom(DamageSource.outOfWorld, Integer.MAX_VALUE);
    }

    /**
     * Updates the arm swing progress counters and animation progress
     */
    protected void updateArmSwingProgress()
    {
        int i = this.getArmSwingAnimationEnd();

        if (this.swingingItem)
        {
            ++this.swingTimer;

            if (this.swingTimer >= i)
            {
                this.swingTimer = 0;
                this.swingingItem = false;
            }
        }
        else
        {
            this.swingTimer = 0;
        }

        this.swingFactor = (float)this.swingTimer / (float)i;
    }

    public final AttributeMap getAttributeMap()
    {
        return this.attributes;
    }

//    public CreatureType getCreatureType()
//    {
//        return CreatureType.UNDEFINED;
//    }

    public ItemStack getHeldItem() {
    	return null;
    }
    
    public ItemStack getItem(int slot) {
    	return null;
    }
    
    public ItemStack getArmor(int slotIn) {
    	return null;
    }
//    public abstract void setItem(int slot, ItemStack stack);

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

    /**
     * Returns the volume for the sounds this mob makes.
     */
    protected float getSoundVolume()
    {
        return 1.0F;
    }

//    /**
//     * Gets the pitch of living sounds in living entities.
//     */
//    protected float getSoundPitch()
//    {
//        return (this.rand.floatv() - this.rand.floatv()) * 0.2F + 1.0F;
//    }

    /**
     * Dead and sleeping entities cannot move
     */
    protected boolean isMovementBlocked()
    {
        return this.getHealth() <= 0;
    }

    /**
     * Moves the entity to a position out of the way of its mount.
     */
    public void dismountEntity(Entity entityIn)
    {
        double d0 = entityIn.posX;
        double d1 = entityIn.getEntityBoundingBox().minY + (double)entityIn.height;
        double d2 = entityIn.posZ;
        int i = 1;

        for (int j = -i; j <= i; ++j)
        {
            for (int k = -i; k < i; ++k)
            {
                if (j != 0 || k != 0)
                {
                    int l = (int)(this.posX + (double)j);
                    int i1 = (int)(this.posZ + (double)k);
                    BoundingBox axisalignedbb = this.getEntityBoundingBox().offset((double)j, 1.0D, (double)k);

                    if (this.worldObj.getCollisionBoxes(axisalignedbb).isEmpty())
                    {
                        if (this.worldObj.isBlockSolid(new BlockPos(l, (int)this.posY, i1)))
                        {
                            this.setPositionAndUpdate(this.posX + (double)j, this.posY + 1.0D, this.posZ + (double)k);
                            return;
                        }

                        if (this.worldObj.isBlockSolid(new BlockPos(l, (int)this.posY - 1, i1)) || this.worldObj.getState(new BlockPos(l, (int)this.posY - 1, i1)).getBlock().getMaterial().isColdLiquid())
                        {
                            d0 = this.posX + (double)j;
                            d1 = this.posY + 1.0D;
                            d2 = this.posZ + (double)k;
                        }
                    }
                }
            }
        }

        this.setPositionAndUpdate(d0, d1, d2);
    }

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

    protected float getJumpUpwardsMotion()
    {
        return 0.42F;
    }

    /**
     * Causes this entity to do an upwards motion (jumping).
     */
    public void jump()
    {
        this.motionY = (double)this.getJumpUpwardsMotion();

        if (this.hasEffect(Potion.JUMP))
        {
            this.motionY += (double)((float)(this.getEffect(Potion.JUMP).getAmplifier() + 1) * 0.1F);
        }

        if (this.isSprinting())
        {
            float f = this.rotYaw * 0.017453292F;
            this.motionX -= (double)(ExtMath.sin(f) * 0.2F);
            this.motionZ += (double)(ExtMath.cos(f) * 0.2F);
        }

        this.isAirBorne = true;
    }

    /**
     * main AI tick function, replaces updateEntityActionState
     */
    protected void updateAITick()
    {
        this.motionY += 0.03999999910593033D;
    }

    protected void handleJumpLava()
    {
        this.motionY += 0.03999999910593033D;
    }

    /**
     * Moves the entity based on the specified heading.  Args: strafe, forward
     */
    public void moveEntityWithHeading(float strafe, float forward)
    {
        if (this.isTicked())
        {
            if (!this.isInLiquid() || this.isPlayer() && ((EntityNPC)this).flying)
            {
                if (!this.isInMolten() || this.isPlayer() && ((EntityNPC)this).flying)
                {
                    float f4 = 0.91F;

                    if (this.onGround)
                    {
                        f4 = this.worldObj.getState(new BlockPos(ExtMath.floord(this.posX), ExtMath.floord(this.getEntityBoundingBox().minY) - 1, ExtMath.floord(this.posZ))).getBlock().slipperiness * 0.91F;
                    }

                    float f = 0.16277136F / (f4 * f4 * f4);
                    float f5;

                    if (this.onGround)
                    {
                        f5 = this.getAIMoveSpeed() * f;
                    }
                    else
                    {
                        f5 = this.jumpMovement;
                    }

                    this.moveFlying(strafe, forward, f5);
                    f4 = 0.91F;

                    if (this.onGround)
                    {
                        f4 = this.worldObj.getState(new BlockPos(ExtMath.floord(this.posX), ExtMath.floord(this.getEntityBoundingBox().minY) - 1, ExtMath.floord(this.posZ))).getBlock().slipperiness * 0.91F;
                    }

                    if (this.isOnLadder())
                    {
                        float f6 = 0.15F;
                        this.motionX = ExtMath.clampd(this.motionX, (double)(-f6), (double)f6);
                        this.motionZ = ExtMath.clampd(this.motionZ, (double)(-f6), (double)f6);
                        this.fallDistance = 0.0F;

                        if (this.motionY < -0.15D)
                        {
                            this.motionY = -0.15D;
                        }

                        boolean flag = this.isSneaking() && this.isPlayer();

                        if (flag && this.motionY < 0.0D)
                        {
                            this.motionY = 0.0D;
                        }
                    }

                    this.moveEntity(this.motionX, this.motionY, this.motionZ);

                    if (this.collidedHorizontally && this.isOnLadder())
                    {
                        this.motionY = 0.2D;
                    }

                    if (this.worldObj.client && (!this.worldObj.isBlockLoaded(new BlockPos((int)this.posX, 0, (int)this.posZ)) || !this.worldObj.getChunk(new BlockPos((int)this.posX, 0, (int)this.posZ)).isLoaded()) && !this.isPlayer())
                    {
                        if (this.posY > 0.0D)
                        {
                            this.motionY = -0.1D;
                        }
                        else
                        {
                            this.motionY = 0.0D;
                        }
                    }
                    else
                    {
                        this.motionY -= 0.08D * this.worldObj.gravity;
                    }

                    this.motionY *= 0.9800000190734863D;
                    this.motionX *= (double)f4;
                    this.motionZ *= (double)f4;
                }
                else
                {
                    double d1 = this.posY;
                    this.moveFlying(strafe, forward, 0.02F);
                    this.moveEntity(this.motionX, this.motionY, this.motionZ);
                    this.motionX *= 0.5D;
                    this.motionY *= 0.5D;
                    this.motionZ *= 0.5D;
                    this.motionY -= 0.02D * this.worldObj.gravity;

                    if (this.collidedHorizontally && this.isOffsetPositionInLiquid(this.motionX, this.motionY + 0.6000000238418579D - this.posY + d1, this.motionZ))
                    {
                        this.motionY = 0.30000001192092896D;
                    }
                }
            }
            else
            {
                double d0 = this.posY;
                float f1 = 0.8F;
                float f2 = 0.02F;
                float f3 = // (float)EnchantmentHelper.getDepthStriderModifier(this);

//                if (f3 > 3.0F)
//                {
//                    f3 = 
                    3.0F;
//                }

                if (!this.onGround)
                {
                    f3 *= 0.5F;
                }

                if (f3 > 0.0F)
                {
                    f1 += (0.54600006F - f1) * f3 / 3.0F;
                    f2 += (this.getAIMoveSpeed() * 1.0F - f2) * f3 / 3.0F;
                }

                this.moveFlying(strafe, forward, f2);
                this.moveEntity(this.motionX, this.motionY, this.motionZ);
                this.motionX *= (double)f1;
                this.motionY *= 0.800000011920929D;
                this.motionZ *= (double)f1;
                this.motionY -= 0.02D * this.worldObj.gravity;

                if (this.collidedHorizontally && this.isOffsetPositionInLiquid(this.motionX, this.motionY + 0.6000000238418579D - this.posY + d0, this.motionZ))
                {
                    this.motionY = 0.30000001192092896D;
                }
            }
        }

        this.prevLswingAmount = this.lswingAmount;
        double d2 = this.posX - this.prevX;
        double d3 = this.posZ - this.prevZ;
        float f7 = ExtMath.sqrtd(d2 * d2 + d3 * d3) * 4.0F;

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

        this.lswingAmount += (f7 - this.lswingAmount) * 0.4F;
        this.limbSwing += this.lswingAmount;
    }

    /**
     * the movespeed used for the new AI system
     */
    public float getAIMoveSpeed()
    {
        return this.landMovement;
    }
    
    public float getSpeedBase() {
    	return this.baseSpeed;
    }
    
    public void setSpeedBase(float value) {
    	this.baseSpeed = value;
    }
    
    public final void setSpeedMod(float value) {
    	this.modSpeed = value;
    }
    
    public float getMovementSpeed() {
    	float speed = this.getSpeedBase() * (this.isSprinting() ? 1.3f : 1.0f);
    	if(this.hasEffect(Potion.SPEED))
    		speed *= 1.0f + (float)(this.getEffect(Potion.SPEED).getAmplifier() + 1) * 0.2f;
    	if(this.hasEffect(Potion.SLOWNESS))
    		speed *= Math.max(1.0f - (float)(this.getEffect(Potion.SLOWNESS).getAmplifier() + 1) * 0.15f, 0.0f);
    	return Math.max(this.modSpeed * speed, 0.0f);
    }

    /**
     * set the movespeed used for the new AI system
     */
    public void setAIMoveSpeed(float speedIn)
    {
        this.landMovement = speedIn;
		this.setMoveForward(speedIn);
    }

    public boolean attackEntityAsMob(Entity entityIn)
    {
        this.setLastAttack(entityIn);
        return false;
    }

    /**
     * Returns whether player is sleeping or not
     */
//    public boolean isPlayerSleeping()
//    {
//        return false;
//    }

    /**
     * Called to update the entity's position/logic.
     */
    public void onUpdate()
    {
        super.onUpdate();

        if (!this.worldObj.client)
        {
            int i = this.getArrowCountInEntity();

            if (i > 0)
            {
                if (this.arrowTimer <= 0)
                {
                    this.arrowTimer = 20 * (30 - i);
                }

                --this.arrowTimer;

                if (this.arrowTimer <= 0)
                {
                    this.setArrowCountInEntity(i - 1);
                }
            }

//            for (int j = 0; j < 5; ++j)
//            {
//                ItemStack itemstack = this.prevEquipment[j];
//                ItemStack itemstack1 = this.getItem(j);
//
//                if (!ItemStack.areItemStacksEqual(itemstack1, itemstack))
//                {
//                    ((IWorldServer)this.worldObj).sendToAllTrackingEntity(this, new SPacketEntityEquipment(this.getId(), j, itemstack1));
//
//                    if (itemstack != null)
//                    {
//                        this.attributes.removeAttributeModifiers(itemstack.getAttributeModifiers(1));
//                    }
//
//                    if (itemstack1 != null)
//                    {
//                        this.attributes.applyAttributeModifiers(itemstack1.getAttributeModifiers(1));
//                    }
//
//                    this.prevEquipment[j] = itemstack1 == null ? null : itemstack1.copy();
//                }
//            }

            if (this.ticksExisted % 20 == 0)
            {
                this.resetCombat();
            }
        }

        this.onLivingUpdate();
        double d0 = this.posX - this.prevX;
        double d1 = this.posZ - this.prevZ;
        float f = (float)(d0 * d0 + d1 * d1);
        float f1 = this.yawOffset;
        float f2 = 0.0F;
        this.prevGroundFactor = this.groundFactor;
        float f3 = 0.0F;

        if (f > 0.0025000002F)
        {
            f3 = 1.0F;
            f2 = (float)Math.sqrt((double)f) * 3.0F;
            f1 = (float)ExtMath.atan2(d1, d0) * 180.0F / (float)Math.PI - 90.0F;
        }

        if (this.swingFactor > 0.0F)
        {
            f1 = this.rotYaw;
        }

        if (!this.onGround)
        {
            f3 = 0.0F;
        }

        this.groundFactor += (f3 - this.groundFactor) * 0.3F;
//        this.worldObj.profiler.start("headTurn");
        f2 = this.updateDistance(f1, f2);
//        this.worldObj.profiler.end();
//        this.worldObj.profiler.start("rangeChecks");

        while (this.rotYaw - this.prevYaw < -180.0F)
        {
            this.prevYaw -= 360.0F;
        }

        while (this.rotYaw - this.prevYaw >= 180.0F)
        {
            this.prevYaw += 360.0F;
        }

        while (this.yawOffset - this.prevYawOffset < -180.0F)
        {
            this.prevYawOffset -= 360.0F;
        }

        while (this.yawOffset - this.prevYawOffset >= 180.0F)
        {
            this.prevYawOffset += 360.0F;
        }

        while (this.rotPitch - this.prevPitch < -180.0F)
        {
            this.prevPitch -= 360.0F;
        }

        while (this.rotPitch - this.prevPitch >= 180.0F)
        {
            this.prevPitch += 360.0F;
        }

        while (this.headYaw - this.prevHeadYaw < -180.0F)
        {
            this.prevHeadYaw -= 360.0F;
        }

        while (this.headYaw - this.prevHeadYaw >= 180.0F)
        {
            this.prevHeadYaw += 360.0F;
        }

//        this.worldObj.profiler.end();
        this.movedDist += f2;

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

    /**
     * Called frequently so the entity can update its state every tick as required. For example, zombies and skeletons
     * use this to react to sunlight and start to burn.
     */
    public void onLivingUpdate()
    {
        if (this.jumpTimer > 0)
        {
            --this.jumpTimer;
        }

        if (this.moveIncrements > 0)
        {
            double d0 = this.posX + (this.newX - this.posX) / (double)this.moveIncrements;
            double d1 = this.posY + (this.newY - this.posY) / (double)this.moveIncrements;
            double d2 = this.posZ + (this.newZ - this.posZ) / (double)this.moveIncrements;
            double d3 = ExtMath.wrapd(this.newYaw - (double)this.rotYaw);
            this.rotYaw = (float)((double)this.rotYaw + d3 / (double)this.moveIncrements);
            this.rotPitch = (float)((double)this.rotPitch + (this.newPitch - (double)this.rotPitch) / (double)this.moveIncrements);
            --this.moveIncrements;
            this.setPosition(d0, d1, d2);
            this.setRotation(this.rotYaw, this.rotPitch);
        }
        else if (!this.isTicked())
        {
            this.motionX *= 0.98D;
            this.motionY *= 0.98D;
            this.motionZ *= 0.98D;
        }

        if (Math.abs(this.motionX) < 0.005D)
        {
            this.motionX = 0.0D;
        }

        if (Math.abs(this.motionY) < 0.005D)
        {
            this.motionY = 0.0D;
        }

        if (Math.abs(this.motionZ) < 0.005D)
        {
            this.motionZ = 0.0D;
        }

//        this.worldObj.profiler.start("ai");

        if (this.isMovementBlocked())
        {
            this.jumping = false;
            this.moveStrafe = 0.0F;
            this.moveForward = 0.0F;
            this.randomYawVelo = 0.0F;
        }
        else if (this.isTicked())
        {
//            this.worldObj.profiler.start("newAi");
            this.updateEntityActionState();
//            this.worldObj.profiler.end();
        }

//        this.worldObj.profiler.end();
//        this.worldObj.profiler.start("jump");

        if (this.jumping)
        {
            if (this.isInLiquid())
            {
                this.updateAITick();
            }
            else if (this.isInMolten())
            {
                this.handleJumpLava();
            }
            else if (this.onGround && this.jumpTimer == 0)
            {
                this.jump();
                this.jumpTimer = 10;
            }
        }
        else
        {
            this.jumpTimer = 0;
        }

//        this.worldObj.profiler.end();
//        this.worldObj.profiler.start("travel");
        this.moveStrafe *= 0.98F;
        this.moveForward *= 0.98F;
        this.randomYawVelo *= 0.9F;
        this.moveEntityWithHeading(this.moveStrafe, this.moveForward);
//        this.worldObj.profiler.end();
//        this.worldObj.profiler.start("push");

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

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

        if (this.worldObj.client)
        {
            if (this.particleTimer > 0)
            {
                if (this.particleTimer % 4 == 0)
                {
                    this.worldObj.spawnParticle(ParticleType.GROW, this.posX + (double)(this.rand.floatv() * this.width * 2.0F) - (double)this.width, this.posY + 0.5D + (double)(this.rand.floatv() * this.height), this.posZ + (double)(this.rand.floatv() * this.width * 2.0F) - (double)this.width, 0.0D, 0.0D, 0.0D);
                }

                --this.particleTimer;
            }

            this.setScaleForAge();
        }
        else
        {
            int i = this.getGrowingAge();

            if (i < 0)
            {
                ++i;
                this.setGrowingAge(i);
            }
            else if (i > 0)
            {
                --i;
                this.setGrowingAge(i);
            }
        }
    }

//    protected void updateEntityActionState()
//    {
//    }

    protected void collideWithNearbyEntities()
    {
        List<Entity> list = this.worldObj.getEntitiesInAABBexcluding(this, this.getEntityBoundingBox().expand(0.20000000298023224D, 0.0D, 0.20000000298023224D), new Predicate<Entity>()
        {
            public boolean test(Entity p_apply_1_)
            {
                return p_apply_1_.canBePushed();
            }
        });

        if (!list.isEmpty())
        {
            for (int i = 0; i < list.size(); ++i)
            {
                Entity entity = (Entity)list.get(i);
                this.collideWithEntity(entity);
            }
        }
    }

    protected void collideWithEntity(Entity entityIn)
    {
        entityIn.applyEntityCollision(this);
    }

    /**
     * Called when a player mounts an entity. e.g. mounts a pig, mounts a boat.
     */
    public void mountEntity(Entity entityIn)
    {
        if (this.vehicle != null && entityIn == null)
        {
            if (!this.worldObj.client)
            {
                this.dismountEntity(this.vehicle);
            }

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

            this.vehicle = null;
        }
        else
        {
            super.mountEntity(entityIn);
        }
    }

    /**
     * Handles updating while being ridden by an entity
     */
    public void updateRidden()
    {
        super.updateRidden();
        this.prevGroundFactor = this.groundFactor;
        this.groundFactor = 0.0F;
        this.fallDistance = 0.0F;
    }

    public void setClientPosition(double x, double y, double z, float yaw, float pitch, boolean teleport)
    {
        this.newX = x;
        this.newY = y;
        this.newZ = z;
        this.newYaw = (double)yaw;
        this.newPitch = (double)pitch;
        this.moveIncrements = 3;
    }

    public void setJumping(boolean jumping)
    {
        this.jumping = jumping;
    }

    /**
     * Called whenever an item is picked up from walking over it. Args: pickedUpEntity, stackSize
     */
    public void onItemPickup(Entity entity, int amount)
    {
        if (!entity.dead && !this.worldObj.client)
        {
        	AWorldServer world = ((AWorldServer)this.worldObj);

            if (entity instanceof EntityItem)
            {
                world.sendToAllTrackingEntity(entity, new SPacketCollectItem(entity.getId(), this.getId()));
            }

            if (entity instanceof EntityArrow)
            {
                world.sendToAllTrackingEntity(entity, new SPacketCollectItem(entity.getId(), this.getId()));
            }

            if (entity instanceof EntityXp)
            {
                world.sendToAllTrackingEntity(entity, new SPacketCollectItem(entity.getId(), this.getId()));
            }
        }
    }

    /**
     * returns true if the entity provided in the argument can be seen. (Raytrace)
     */
    public boolean canEntityBeSeen(Entity entityIn)
    {
        return this.worldObj.rayTraceBlocks(new Vec3(this.posX, this.posY + (double)this.getEyeHeight(), this.posZ), new Vec3(entityIn.posX, entityIn.posY + (double)entityIn.getEyeHeight(), entityIn.posZ)) == null;
    }

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

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

    /**
     * Returns where in the swing animation the living entity is (from 0 to 1).  Args: partialTickTime
     */
    public float getSwingProgress(float partialTickTime)
    {
        float f = this.swingFactor - this.prevSwingFactor;

        if (f < 0.0F)
        {
            ++f;
        }

        return this.prevSwingFactor + f * partialTickTime;
    }
    
//    public boolean isTicked()
//    {
//        return !this.worldObj.client;
//    }

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

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

    /**
     * Sets that this entity has been attacked.
     */
    protected void setBeenAttacked()
    {
        this.veloChanged = !this.hasEffect(Potion.STABILITY) && this.rand.doublev() >= this.getKnockBackResistance();
    }

    public float getRotationYawHead()
    {
        return this.headYaw;
    }

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

    /**
     * Set the render yaw offset
     *  
     * @param offset The render yaw offset
     */
    public void setRenderYawOffset(float offset)
    {
        this.yawOffset = offset;
    }

    public int getAbsorptionAmount()
    {
        return this.absorptionAmount;
    }

    public void setAbsorptionAmount(int amount)
    {
        if (amount < 0)
        {
            amount = 0;
        }

        this.absorptionAmount = amount;
    }

//    /**
//     * Sends an ENTER_COMBAT packet to the client
//     */
//    public void sendEnterCombat()
//    {
//    }
//
//    /**
//     * Sends an END_COMBAT packet to the client
//     */
//    public void sendEndCombat()
//    {
//    }

    protected void markEffectsDirty()
    {
        this.effectsDirty = true;
    }
    
    public static TextColor getHealthColor(int health, int max) {
    	float perc = (float)health / (float)max;
    	return /* health <= 1.0f ? ChatFormat.DARK_RED : ( */ perc >= 1.0f ? TextColor.DGREEN : (perc >= 0.75f ? TextColor.GREEN : 
    		(perc >= 0.5f ? TextColor.YELLOW : (perc >= 0.25f ? TextColor.ORANGE : (perc >= 0.1f ? TextColor.RED : TextColor.DRED))));
    }
    
    public static TextColor getMaxHpColor(int max) {
    	return max >= 2000 ? TextColor.BLACK : (max >= 500 ? TextColor.DMAGENTA : (max >= 120 ? TextColor.MAGENTA : 
    		(max >= 40 ? TextColor.CYAN : (max >= 15 ? TextColor.NEON : (max >= 5 ? TextColor.BLUE : TextColor.LGRAY)))));
    }

	public int getEnergy(Energy type) {
		return 0;
	}

	public int getEnergyCap(Energy type) {
		return 0;
	}

	public boolean hasEnergyEffect(Energy type) {
		return false;
	}

	public boolean isEnergyDrawn(Energy type) {
		return false;
	}
    
    public String formatStats() {
    	return String.format(this.getAlignment().prefix + " " + getHealthColor(this.getHealth(), this.getMaxHealth()) + "%d" + 
    			TextColor.GRAY + "/" + getMaxHpColor(this.getMaxHealth()) + "%d", 
    			this.getHealth(), this.getMaxHealth());
    }
    
    public abstract int getColor();
    
    public boolean isGhost() {
    	return false;
    }
    
    public boolean isGlowing() {
    	return false;
    }
    
    public void addRadiation(float rad) {
    	this.radiation += rad;
    }
    
    public void clearRadiation() {
    	this.radiation = 0.0f;
    }

	public void trackDamage(DamageSource source, int amount) {
		this.resetCombat();
		this.blockType = null;
		if(this.isOnLadder()) {
			Block block = this.worldObj
					.getState(new BlockPos(this.posX, this.getEntityBoundingBox().minY, this.posZ)).getBlock();
			if(block == Blocks.ladder)
				this.blockType = "von einer Leiter";
			else if(block == Blocks.vine)
				this.blockType = "von Ranken";
		}
		else if(this.isInLiquid()) {
			this.blockType = "aus dem Wasser";
		}
		CombatEntry entry = new CombatEntry(source, amount, this.blockType, source == DamageSource.outOfWorld ? Float.MAX_VALUE : this.fallDistance);
		this.combat.add(entry);
		this.lastDamaged = this.ticksExisted;
		this.damaged = true;
		if(entry.source().getEntity() instanceof EntityLiving && !this.attacked && this.isEntityAlive())
			this.attacked = true;
	}

	protected void sendDeathMessage() {
		this.sendDeathMessage(false, false);
	}

	protected void sendDeathMessage(boolean natural, boolean forAll) {
		if(this.worldObj.client)
			return;
		String msg;
		String kill;
		IPlayer receiver = null;
		if(this.combat.size() == 0) {
			msg = kill = natural ? String.format("%s starb", this.getColoredName(TextColor.LGRAY)) : null;
		}
		else {
			CombatEntry strong = null;
			CombatEntry block = null;
			int min = 0;
			float max = 0.0F;

			for(int z = 0; z < this.combat.size(); ++z) {
				CombatEntry entry = (CombatEntry)this.combat.get(z);
				CombatEntry last = z > 0 ? (CombatEntry)this.combat.get(z - 1) : null;

				if((entry.source() == DamageSource.fall || entry.source() == DamageSource.outOfWorld) && 
						entry.fallDistance() > 0.0F && (strong == null || entry.fallDistance() > max)) {
					if(z > 0) {
						strong = last;
					}
					else {
						strong = entry;
					}

					max = entry.fallDistance();
				}

				if(entry.blockType() != null && (block == null || entry.damage() > min)) {
					block = entry;
				}
			}
			CombatEntry fall = max > 5.0F && strong != null ? strong : (min > 5 && block != null ? block : null);
			CombatEntry last = (CombatEntry)this.combat.get(this.combat.size() - 1);
			Entity lastEnt = last.source().getEntity();

			if(fall != null && last.source() == DamageSource.fall) {
				if(fall.source() != DamageSource.fall && fall.source() != DamageSource.outOfWorld) {
					Entity fallEnt = fall.source().getEntity();
					if(fallEnt != null && (lastEnt == null || fallEnt != lastEnt)) {
						ItemStack fallItem = fallEnt instanceof EntityLiving ? ((EntityLiving)fallEnt).getHeldItem() : null;
						receiver = fallEnt.isPlayer() ? ((EntityNPC)fallEnt).connection : null;
						if(fallItem != null) { // && fallItem.hasDisplayName()) {
							msg = String.format("%s wurde von %s mit %s zum Fallen verdammt", this.getColoredName(TextColor.CYAN), 
									fallEnt.getColoredName(TextColor.CYAN), fallItem.getColoredName(TextColor.CYAN));
							kill = String.format(TextColor.CYAN + "* %s mit %s zum Fallen verdammt", 
									this.getColoredName(TextColor.CYAN), fallItem.getColoredName(TextColor.CYAN));
						}
						else {
							msg = String.format("%s wurde von %s zum Fallen verdammt", this.getColoredName(TextColor.CYAN), 
									fallEnt.getColoredName(TextColor.CYAN));
							kill = String.format(TextColor.CYAN + "* %s zum Fallen verdammt", 
									this.getColoredName(TextColor.CYAN));
						}
					}
					else if(lastEnt != null) {
						ItemStack lastItem = lastEnt instanceof EntityLiving ? ((EntityLiving)lastEnt).getHeldItem() : null;
						receiver = lastEnt.isPlayer() ? ((EntityNPC)lastEnt).connection : null;
						if(lastItem != null) { //  && lastItem.hasDisplayName()) {
							msg = String.format("%s fiel zu tief und wurde von %s mit %s erledigt", 
									this.getColoredName(TextColor.BLUE), 
									lastEnt.getColoredName(TextColor.BLUE), lastItem.getColoredName(TextColor.BLUE));
							kill = String.format(TextColor.BLUE + "* %s mit %s erledigt", 
									this.getColoredName(TextColor.BLUE), lastItem.getColoredName(TextColor.BLUE));
						}
						else {
							msg = String.format("%s fiel zu tief und wurde von %s erledigt", this.getColoredName(TextColor.BLUE), 
									lastEnt.getColoredName(TextColor.BLUE));
							kill = String.format(TextColor.BLUE + "%s erledigt", this.getColoredName(TextColor.BLUE));
						}
					}
					else {
						msg = kill = natural ? String.format("%s wurde zum Fallen verdammt", this.getColoredName(TextColor.CYAN)) : null;
					}
				}
				else {
					msg = kill = natural ? String.format("%s fiel " + (fall.blockType() == null ? "aus zu großer Höhe" : fall.blockType()),
							this.getColoredName(TextColor.NEON)) : null;
				}
			}
			else {
				receiver = last.source().getEntity() != null && last.source().getEntity().isPlayer() ? ((EntityNPC)last.source().getEntity()).connection : null;
				msg = natural || (last.source() instanceof EntityDamageSource ? last.source().getEntity() != null : this.getAttackingEntity() != null) ? last.source().getDeathMessage(this) : null;
				kill = msg == null ? null : last.source().getKillMessage(this);
			}
		}
		if(msg == null)
			return;
		if(receiver != null)
			receiver.addFeed(kill);
		if(forAll)
			for(IPlayer player : ((AWorldServer)this.worldObj).getAllPlayers()) {
				if(player != receiver)
					player.addFeed(msg);
			}
	}

	public EntityLiving getAttacking() {
		EntityLiving entity = null;
		EntityNPC player = null;
		int edmg = 0;
		int pdmg = 0;
		for(CombatEntry entry : this.combat) {
			if(entry.source().getEntity() != null && entry.source().getEntity().isPlayer() && (player == null || entry.damage() > pdmg)) {
				pdmg = entry.damage();
				player = (EntityNPC)entry.source().getEntity();
			}
			if(entry.source().getEntity() instanceof EntityLiving && (entity == null || entry.damage() > edmg)) {
				edmg = entry.damage();
				entity = (EntityLiving)entry.source().getEntity();
			}
		}
		return player != null && pdmg >= edmg / 3 ? player : entity;
	}

	public void resetCombat() {
		int timeout = this.attacked ? 300 : 100;
		if(this.damaged && (!this.isEntityAlive() || this.ticksExisted - this.lastDamaged > timeout)) {
			this.damaged = false;
			this.attacked = false;
			this.combat.clear();
		}
	}
	
    public boolean isSneakingVisually() {
        return this.isSneaking();
    }
    
    public float getArmRotation() {
    	return 1.0f;
    }
    
    public float getLegRotation() {
    	return 1.4f;
    }
    
    // entityliving merge ...
    


//	public void handleStatusUpdate(byte id) {
//		if(id == 20) {
//			this.spawnExplosionParticle();
//		}
//		else {
//			super.handleStatusUpdate(id);
//		}
//	}

	public void spawnExplosionParticle() {
		if(this.worldObj.client) {
			for(int i = 0; i < 20; ++i) {
				double d0 = this.rand.gaussian() * 0.02D;
				double d1 = this.rand.gaussian() * 0.02D;
				double d2 = this.rand.gaussian() * 0.02D;
				double d3 = 10.0D;
				this.worldObj.spawnParticle(ParticleType.EXPLOSION_NORMAL,
						this.posX + (double)(this.rand.floatv() * this.width * 2.0F) - (double)this.width - d0 * d3,
						this.posY + (double)(this.rand.floatv() * this.height) - d1 * d3,
						this.posZ + (double)(this.rand.floatv() * this.width * 2.0F) - (double)this.width - d2 * d3, d0, d1, d2);
			}
		}
		else {
			this.worldObj.setEntityState(this, (byte)20);
		}
	}

	protected SoundEvent getLivingSound() {
		return null;
	}

	protected Item getDropItem() {
		return null;
	}

	protected void dropFewItems(boolean wasRecentlyHit, int lootingModifier) {
		Item item = this.getDropItem();

		if(item != null) {
			int i = this.rand.zrange(3);

			if(lootingModifier > 0) {
				i += this.rand.zrange(lootingModifier + 1);
			}

			for(int j = 0; j < i; ++j) {
				this.dropItem(item, 1);
			}
		}
	}

	protected int getExperiencePoints(EntityNPC player) {
//		if(this.xpValue > 0) {
//			int i = this.xpValue;
//			ItemStack[] aitemstack = this.getInventory();
//
//			for(int j = 0; j < aitemstack.length; ++j) {
//				if(aitemstack[j] != null && this.dropChances[j] <= 1.0F) {
//					i += this.rand.roll(3);
//				}
//			}
//
//			return i;
//		}
//		else {
		return this.xpValue;
//		}
	}

//	protected void entityInit() {
//		super.entityInit();
//		this.dataWatcher.addObject(15, Byte.valueOf((byte)0));
//	}

	public int getTalkInterval() {
		return 80;
	}

	public void playLivingSound() {
		SoundEvent s = this.getLivingSound();

		if(s != null) {
			this.playSound(s, this.getSoundVolume());
		}
	}

//	public void onLivingUpdate() {
//		super.onLivingUpdate();
////		this.worldObj.profiler.start("looting");
//
//		if(!this.worldObj.client && this.canPickUpLoot() && !this.noPickup && Config.mobGriefing) {
//			for(EntityItem entityitem : this.worldObj.getEntitiesWithinAABB(EntityItem.class, this.getEntityBoundingBox().expand(1.0D, 0.0D, 1.0D))) {
//				if(!entityitem.dead && entityitem.getEntityItem() != null && !entityitem.cannotPickup()) {
//					this.updateEquipmentIfNeeded(entityitem);
//				}
//			}
//		}
//
////		this.worldObj.profiler.end();
//	}

//	protected boolean canDespawn() {
//		return true;
//	}

	public void setMoveForward(float p_70657_1_) {
		this.moveForward = p_70657_1_;
	}

//	public void setAIMoveSpeed(float speedIn) {
//		super.setAIMoveSpeed(speedIn);
//		this.setMoveForward(speedIn);
//	}
	
	public int getPathingRange() {
		return 16;
	}

	public Object onInitialSpawn(Object livingdata) {
		return livingdata;
	}

	public boolean isPropogatingSpawnData(boolean newGroup) {
		return !newGroup;
	}

	public boolean canBeSteered() {
		return false;
	}

//	public void disableDespawn() {
//		this.noDespawn = true;
//	}
//
//	public boolean isNotDespawning() {
//		return this.noDespawn;
//	}

	public boolean isTicked() {
		return !this.worldObj.client && /* !this.isAIDisabled() && */ Vars.mobTick;
	}

//	public void setNoAI(boolean disable) {
//		this.dataWatcher.updateObject(15, Byte.valueOf((byte)(disable ? 1 : 0)));
//	}
//
//	public boolean isAIDisabled() {
//		return this.dataWatcher.getWatchableObjectByte(15) != 0;
//	}

	public int getLeashColor() {
		return 0x7f664c;
	}
	
//	public void onDeath(DamageSource cause) {
//		if(!(this.isPlayer())) {
//	        EntityLivingBase entity = this.getAttackingEntity();
//	        if(entity.isPlayer())
//	        	((EntityNPCMP)entity).addToPlayerScore(this);
//		}
//        super.onDeath(cause);
//	}

	public boolean isBoss() {
		return false;
	}

	public boolean interactFirst(EntityNPC player) {
		if(this.getLeashed() && this.getLeashedTo() == player) {
			this.clearLeashed(true, true); // !player.creative);
			return true;
		}
		else {
			ItemStack stack = player.inventory.getCurrentItem();

			if(stack != null && stack.getItem() == Items.lead && this.allowLeashing()) {
				if(!(this instanceof EntityTameable) || !((EntityTameable)this).isTamed()) {
					this.setLeashedTo(player, true);
					--stack.size;
					return true;
				}

				if(((EntityTameable)this).isOwner(player)) {
					this.setLeashedTo(player, true);
					--stack.size;
					return true;
				}
			}

			if(this.interact(player)) {
				return true;
			}
			else {
				return super.interactFirst(player);
			}
		}
	}

//	protected boolean interact(EntityNPC player) {
//		return false;
//	}

	public int getMaxChunkSpawns() {
		return 4;
	}

	public int getMaxFallHeight() {
		if(this.getAttackTarget() == null) {
			return 3;
		}
		else {
			int i = (int)((float)this.getHealth() - (float)this.getMaxHealth() * 0.33F);
//			i = i - (3 - this.worldObj.getDifficulty().getId()) * 4;

			if(i < 0) {
				i = 0;
			}

			return i + 3;
		}
	}

//	public boolean getCanSpawnHere() {
//		return true;
//	}

//	public boolean isNotColliding() {
//		return this.worldObj.checkNoEntityCollision(this.getEntityBoundingBox(), this)
//				&& this.worldObj.getCollidingBoundingBoxes(this, this.getEntityBoundingBox()).isEmpty()
//				&& !this.worldObj.isAnyLiquid(this.getEntityBoundingBox());
//	}
	
    public boolean isNotColliding() {
        return this.worldObj.checkNoEntityCollision(this.getEntityBoundingBox(), this) && 
        		this.worldObj.getCollidingBoundingBoxes(this, this.getEntityBoundingBox()).isEmpty() &&
        		(this.canSpawnInLiquid() || !this.worldObj.isAnyLiquid(this.getEntityBoundingBox()));
    }
    
    public boolean canSpawnInLiquid() {
    	return false;
    }

	protected void updateAITasks() {
	}

	public int getVerticalFaceSpeed() {
		return 40;
	}

	public void faceEntity(Entity entityIn, float p_70625_2_, float p_70625_3_) {
		double d0 = entityIn.posX - this.posX;
		double d2 = entityIn.posZ - this.posZ;
		double d1;

		if(entityIn instanceof EntityLiving) {
			EntityLiving entitylivingbase = (EntityLiving)entityIn;
			d1 = entitylivingbase.posY + (double)entitylivingbase.getEyeHeight() - (this.posY + (double)this.getEyeHeight());
		}
		else {
			d1 = (entityIn.getEntityBoundingBox().minY + entityIn.getEntityBoundingBox().maxY) / 2.0D - (this.posY + (double)this.getEyeHeight());
		}

		double d3 = (double)ExtMath.sqrtd(d0 * d0 + d2 * d2);
		float f = (float)(ExtMath.atan2(d2, d0) * 180.0D / Math.PI) - 90.0F;
		float f1 = (float)(-(ExtMath.atan2(d1, d3) * 180.0D / Math.PI));
		this.rotPitch = this.updateRotation(this.rotPitch, f1, p_70625_3_);
		this.rotYaw = this.updateRotation(this.rotYaw, f, p_70625_2_);
	}

	private float updateRotation(float p_70663_1_, float p_70663_2_, float p_70663_3_) {
		float f = ExtMath.wrapf(p_70663_2_ - p_70663_1_);

		if(f > p_70663_3_) {
			f = p_70663_3_;
		}

		if(f < -p_70663_3_) {
			f = -p_70663_3_;
		}

		return p_70663_1_ + f;
	}

	protected void updateEntityActionState() {
//		++this.entityAge;
//		this.worldObj.profiler.start("checkDespawn");
//		this.despawnEntity();
//		this.worldObj.profiler.end();
//		this.worldObj.profiler.start("sensing");
		this.senses.clearSensingCache();
//		this.worldObj.profiler.end();
//		this.worldObj.profiler.start("targetSelector");
		this.targets.onUpdateTasks();
//		this.worldObj.profiler.end();
//		this.worldObj.profiler.start("goalSelector");
		this.tasks.onUpdateTasks();
//		this.worldObj.profiler.end();
//		this.worldObj.profiler.start("navigation");
		this.navigator.onUpdateNavigation();
//		this.worldObj.profiler.end();
//		this.worldObj.profiler.start("mob tick");
		this.updateAITasks();
//		this.worldObj.profiler.end();
//		this.worldObj.profiler.start("controls");
//		this.worldObj.profiler.start("move");
		this.moveHelper.onUpdateMoveHelper();
//		this.worldObj.profiler.next("look");
		this.lookHelper.onUpdateLook();
//		this.worldObj.profiler.next("jump");
		this.jumpHelper.doJump();
//		this.worldObj.profiler.end();
//		this.worldObj.profiler.end();
	}

//	protected void despawnEntity() {
//		if(this.noDespawn) {
//			this.entityAge = 0;
//		}
//		else {
//			Entity entity = this.worldObj.getClosestPlayerToEntity(this, -1.0D);
//
//			if(entity != null) {
//				double d0 = entity.posX - this.posX;
//				double d1 = entity.posY - this.posY;
//				double d2 = entity.posZ - this.posZ;
//				double d3 = d0 * d0 + d1 * d1 + d2 * d2;
//
//				if(this.canDespawn() && d3 > 16384.0D) {
//					this.setDead();
//				}
//
//				if(this.entityAge > 600 && this.rand.chance(800) && d3 > 1024.0D && this.canDespawn()) {
//					this.setDead();
//				}
//				else if(d3 < 1024.0D) {
//					this.entityAge = 0;
//				}
//			}
//		}
//	}

//	public void onUpdate() {
//		super.onUpdate();
//
//		if(!this.worldObj.client) {
//			this.updateLeashedState();
//		}
//	}

//	public void onEntityUpdate() {
//		super.onEntityUpdate();
////		this.worldObj.profiler.start("mobBaseTick");
//
//		if(this.isEntityAlive() && this.rand.zrange(1000) < this.soundTimer++) {
//			this.soundTimer = -this.getTalkInterval();
//			this.playLivingSound();
//		}
//
//		if(this.target.isPlayer() && ((EntityNPC)this.target).capabilities.isCreativeMode) {
//			this.target = null; // FIX Creative
//		}
//
////		this.worldObj.profiler.end();
//	}

//	protected void applyEntityAttributes() {
//		super.applyEntityAttributes();
//		this.getAttributeMap().registerAttribute(Attributes.FOLLOW_RANGE).setBaseValue(16.0D);
//	}
	
	protected PathNavigate getNewNavigator(World worldIn) {
		return new PathNavigateGround(this, worldIn);
	}

	public EntityLookHelper getLookHelper() {
		return this.lookHelper;
	}

	public EntityMoveHelper getMoveHelper() {
		return this.moveHelper;
	}

	public EntityJumpHelper getJumpHelper() {
		return this.jumpHelper;
	}

	public PathNavigate getNavigator() {
		return this.navigator;
	}

	public EntitySenses getEntitySenses() {
		return this.senses;
	}

	public EntityLiving getAttackTarget() {
		return this.target;
	}

	public void setAttackTarget(EntityLiving entitylivingbaseIn) {
		this.target = entitylivingbaseIn;
	}

//	public boolean canAttackClass(Class<? extends EntityLiving> cls) {
//		return /* cls != EntityFireDemon.class && */
//				;
//	}

	protected void updateLeashedState() {
		if(this.leashTag != null) {
			this.recreateLeash();
		}

		if(this.leashed) {
			if(!this.isEntityAlive()) {
				this.clearLeashed(true, true);
			}

			if(this.leashedTo == null || this.leashedTo.dead) {
				this.clearLeashed(true, true);
			}
		}

        if (this.getLeashed() && this.getLeashedTo() != null && this.getLeashedTo().worldObj == this.worldObj)
        {
            Entity entity = this.getLeashedTo();
            this.setHomePosAndDistance(new BlockPos((int)entity.posX, (int)entity.posY, (int)entity.posZ), 5);
            float f = this.getDistanceToEntity(entity);

            if (this instanceof EntityTameable && ((EntityTameable)this).isSitting())
            {
                if (f > 10.0F)
                {
                    this.clearLeashed(true, true);
                }

                return;
            }

            if (!this.isMovementAITaskSet)
            {
                this.tasks.addTask(2, this.aiBase);

                if (this.getNavigator() instanceof PathNavigateGround)
                {
                    ((PathNavigateGround)this.getNavigator()).setAvoidsWater(false);
                }

                this.isMovementAITaskSet = true;
            }

            this.onUpdateLeashed(f);

            if (f > 4.0F)
            {
                this.getNavigator().tryMoveToEntityLiving(entity, 1.0D);
            }

            if (f > 6.0F)
            {
                double d0 = (entity.posX - this.posX) / (double)f;
                double d1 = (entity.posY - this.posY) / (double)f;
                double d2 = (entity.posZ - this.posZ) / (double)f;
                this.motionX += d0 * Math.abs(d0) * 0.4D;
                this.motionY += d1 * Math.abs(d1) * 0.4D;
                this.motionZ += d2 * Math.abs(d2) * 0.4D;
            }

            if (f > 10.0F)
            {
                this.clearLeashed(true, true);
            }
        }
        else if (!this.getLeashed() && this.isMovementAITaskSet)
        {
            this.isMovementAITaskSet = false;
            this.tasks.removeTask(this.aiBase);

            if (this.getNavigator() instanceof PathNavigateGround)
            {
                ((PathNavigateGround)this.getNavigator()).setAvoidsWater(true);
            }

            this.detachHome();
        }
	}

	public void clearLeashed(boolean pkt, boolean dropLead) {
		if(this.leashed) {
			this.leashed = false;
			this.leashedTo = null;

			if(!this.worldObj.client && dropLead) {
				this.dropItem(Items.lead, 1);
			}

			if(!this.worldObj.client && pkt && this.worldObj instanceof AWorldServer) {
				((AWorldServer)this.worldObj).sendToAllTrackingEntity(this, new SPacketEntityAttach(1, this, (Entity)null));
			}
		}
	}

	public boolean allowLeashing() {
		return !this.getLeashed(); // && !(this instanceof IMob);
	}

	public boolean getLeashed() {
		return this.leashed;
	}

	public Entity getLeashedTo() {
		return this.leashedTo;
	}

	public void setLeashedTo(Entity entity, boolean pkt) {
		this.leashed = true;
		this.leashedTo = entity;

		if(!this.worldObj.client && pkt && this.worldObj instanceof AWorldServer) {
			((AWorldServer)this.worldObj).sendToAllTrackingEntity(this, new SPacketEntityAttach(1, this, this.leashedTo));
		}
	}

	private void recreateLeash() {
		if(this.leashed && this.leashTag != null) {
//			if(this.leashTag.hasString("PlayerName")) {
//				String id = this.leashTag.getString("PlayerName");
//				if(!id.isEmpty()) {
//					for(EntityNPC entitylivingbase : this.worldObj.getEntitiesWithinAABB(EntityNPC.class,
//							this.getEntityBoundingBox().expand(10.0D, 10.0D, 10.0D))) {
//						if(entitylivingbase.getUser().equals(id)) {
//							this.leashedTo = entitylivingbase;
//							break;
//						}
//					}
//				}
//			}
//			else
			if(this.leashTag.hasInt("X") && this.leashTag.hasInt("Y") && this.leashTag.hasInt("Z")) {
				BlockPos blockpos = new BlockPos(this.leashTag.getInt("X"), this.leashTag.getInt("Y"),
						this.leashTag.getInt("Z"));
				EntityLeashKnot entityleashknot = EntityLeashKnot.getKnotForPosition(this.worldObj, blockpos);

				if(entityleashknot == null) {
					entityleashknot = EntityLeashKnot.createKnot(this.worldObj, blockpos);
				}

				this.leashedTo = entityleashknot;
			}
			else {
				this.clearLeashed(false, true);
			}
		}

		this.leashTag = null;
	}

	protected float updateDistance(float p_110146_1_, float p_110146_2_) {
		this.bodyHelper.updateRenderAngles();
		return p_110146_2_;
	}
	
    public ItemStack[] getInventory() {
        return EMPTY_INV;
    }
    
//    public LayerExtra getExtrasLayer() {
//    	return null;
//    }
    
//    public String getLocationSkin() {
//    	return null;
//    }
    
//	public String getLocationCape() {
//    	return null;
//    }
	
//	public boolean canRenderExtras() {
//		return false;
//	}
//	
//	public boolean isWearing(ModelPart part) {
//		return false;
//	}

    protected void spawnParticles(ParticleType particleType)
    {
        for (int i = 0; i < 5; ++i)
        {
            double d0 = this.rand.gaussian() * 0.02D;
            double d1 = this.rand.gaussian() * 0.02D;
            double d2 = this.rand.gaussian() * 0.02D;
            this.worldObj.spawnParticle(particleType, this.posX + (double)(this.rand.floatv() * this.width * 2.0F) - (double)this.width, this.posY + 1.0D + (double)(this.rand.floatv() * this.height), this.posZ + (double)(this.rand.floatv() * this.width * 2.0F) - (double)this.width, d0, d1, d2);
        }
    }
    
    public boolean interact(EntityNPC player)
    {
        ItemStack itemstack = player.inventory.getCurrentItem();

        if (itemstack != null && itemstack.getItem() instanceof ItemMonsterPlacer)
        {
            if (!this.worldObj.client)
            {
//                Class <? extends Entity > oclass = EntityRegistry.getClassFromName(((ItemMonsterPlacer)itemstack.getItem())
//                		.getSpawnedId());
                String oclass = EntityRegistry.getEntityString(this);

                if (oclass != null && oclass.equals(((ItemMonsterPlacer)itemstack.getItem()).getSpawnedId()))
                {
                	EntityLiving child = this.createChild(this);

                    if (child != null)
                    {
                        child.setGrowingAge(-24000);
                        child.setLocationAndAngles(this.posX, this.posY, this.posZ, 0.0F, 0.0F);
                        this.worldObj.spawnEntityInWorld(child);

                        if (itemstack.hasDisplayName())
                        {
                            child.setCustomNameTag(itemstack.getDisplayName());
                        }

//                        if (!player.creative)
//                        {
                        --itemstack.size;

                        if (itemstack.size <= 0)
                        {
                            player.inventory.setInventorySlotContents(player.inventory.currentItem, (ItemStack)null);
                        }
//                        }
                    }
                }
            }

            return true;
        }
        else
        {
            return false;
        }
    }
    
    public int getGrowingAge()
    {
        return this.worldObj.client ? this.dataWatcher.getWatchableObjectInt(7) : this.growingAge;
    }

    public void grow(int amount)
    {
        int age = this.getGrowingAge() + amount * 20;
        this.setGrowingAge(age > 0 ? 0 : age);
        this.worldObj.setEntityState(this, (byte)55);
    }
    
    public void setGrowingAge(int age)
    {
        this.dataWatcher.updateObject(7, Integer.valueOf(ExtMath.clampi(age, -24000, 1)));
        this.growingAge = age;
        this.setScaleForAge();
    }
    
    public void setScaleForAge() {
    }

    public EntityLiving createChild(EntityLiving ageable) {
    	return null;
    }

//	public void handleStatusUpdate(byte id) {
//		if(id == 55) {
//			if(this.particleTimer == 0)
//				this.particleTimer = 40;
//		}
//		else {
//			super.handleStatusUpdate(id);
//		}
//	}

    public float getBlockPathWeight(BlockPos pos)
    {
        return 0.0F;
    }
    
    public boolean getCanSpawnHere()
    {
        return this.getBlockPathWeight(new BlockPos(this.posX, this.getEntityBoundingBox().minY, this.posZ)) >= 0.0F;
    }
    
    public boolean hasPath()
    {
        return !this.navigator.noPath();
    }

    public boolean isWithinHomeDistanceCurrentPosition()
    {
        return this.isWithinHomeDistanceFromPosition(new BlockPos(this));
    }

    public boolean isWithinHomeDistanceFromPosition(BlockPos pos)
    {
        return this.maximumHomeDistance == -1.0F ? true : this.homePosition.distanceSq(pos) < (double)(this.maximumHomeDistance * this.maximumHomeDistance);
    }
    
    public void setHomePosAndDistance(BlockPos pos, int distance)
    {
        this.homePosition = pos;
        this.maximumHomeDistance = (float)distance;
    }

    public BlockPos getHomePosition()
    {
        return this.homePosition;
    }

    public float getMaximumHomeDistance()
    {
        return this.maximumHomeDistance;
    }

    public void detachHome()
    {
        this.maximumHomeDistance = -1.0F;
    }
    
    public boolean hasHome()
    {
        return this.maximumHomeDistance != -1.0F;
    }

    protected void onUpdateLeashed(float distance)
    {
    }
    
    protected void setSize(float width, float height)
    {
        boolean flag = this.ageWidth > 0.0F;
        this.ageWidth = width;
        this.ageHeight = height;

        if (!flag)
        {
            this.setScale(1.0F);
        }
    }
    
    protected void changeSize(float width, float height)
    {
        this.ageWidth = width;
        this.ageHeight = height;
        super.setSize(width, height);
    }

    protected final void setScale(float scale)
    {
        super.setSize(this.ageWidth * scale, this.ageHeight * scale);
    }
    
    public String getName()
    {
    	return this.getAlignment().color + this.getTypeName();
    }
    
    public String getDisplayName()
    {
    	return this.getName();
    }
    
    public abstract Alignment getAlignment();
}
