You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1286 lines
40 KiB
Java
1286 lines
40 KiB
Java
package net.minecraft.entity;
|
|
|
|
import cpw.mods.fml.relauncher.Side;
|
|
import cpw.mods.fml.relauncher.SideOnly;
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
import java.util.UUID;
|
|
import net.minecraft.enchantment.EnchantmentHelper;
|
|
import net.minecraft.entity.ai.EntityAITasks;
|
|
import net.minecraft.entity.ai.EntityJumpHelper;
|
|
import net.minecraft.entity.ai.EntityLookHelper;
|
|
import net.minecraft.entity.ai.EntityMoveHelper;
|
|
import net.minecraft.entity.ai.EntitySenses;
|
|
import net.minecraft.entity.ai.attributes.AttributeModifier;
|
|
import net.minecraft.entity.item.EntityItem;
|
|
import net.minecraft.entity.monster.EntityCreeper;
|
|
import net.minecraft.entity.monster.EntityGhast;
|
|
import net.minecraft.entity.monster.IMob;
|
|
import net.minecraft.entity.passive.EntityTameable;
|
|
import net.minecraft.entity.player.EntityPlayer;
|
|
import net.minecraft.init.Blocks;
|
|
import net.minecraft.init.Items;
|
|
import net.minecraft.item.Item;
|
|
import net.minecraft.item.ItemArmor;
|
|
import net.minecraft.item.ItemStack;
|
|
import net.minecraft.item.ItemSword;
|
|
import net.minecraft.nbt.NBTTagCompound;
|
|
import net.minecraft.nbt.NBTTagFloat;
|
|
import net.minecraft.nbt.NBTTagList;
|
|
import net.minecraft.network.play.server.S1BPacketEntityAttach;
|
|
import net.minecraft.pathfinding.PathNavigate;
|
|
import net.minecraft.stats.AchievementList;
|
|
import net.minecraft.util.MathHelper;
|
|
import net.minecraft.world.EnumDifficulty;
|
|
import net.minecraft.world.World;
|
|
import net.minecraft.world.WorldServer;
|
|
import net.minecraftforge.common.ForgeHooks;
|
|
import cpw.mods.fml.common.eventhandler.Event.Result;
|
|
import net.minecraftforge.event.ForgeEventFactory;
|
|
|
|
public abstract class EntityLiving extends EntityLivingBase
|
|
{
|
|
/** Number of ticks since this EntityLiving last produced its sound */
|
|
public int livingSoundTime;
|
|
/** The experience points the Entity gives. */
|
|
protected int experienceValue;
|
|
private EntityLookHelper lookHelper;
|
|
private EntityMoveHelper moveHelper;
|
|
/** Entity jumping helper */
|
|
private EntityJumpHelper jumpHelper;
|
|
private EntityBodyHelper bodyHelper;
|
|
private PathNavigate navigator;
|
|
public final EntityAITasks tasks;
|
|
public final EntityAITasks targetTasks;
|
|
/** The active target the Task system uses for tracking */
|
|
private EntityLivingBase attackTarget;
|
|
private EntitySenses senses;
|
|
/** Equipment (armor and held item) for this entity. */
|
|
private ItemStack[] equipment = new ItemStack[5];
|
|
/** Chances for each equipment piece from dropping when this entity dies. */
|
|
protected float[] equipmentDropChances = new float[5];
|
|
/** Whether this entity can pick up items from the ground. */
|
|
private boolean canPickUpLoot;
|
|
/** Whether this entity should NOT despawn. */
|
|
private boolean persistenceRequired;
|
|
protected float defaultPitch;
|
|
/** This entity's current target. */
|
|
private Entity currentTarget;
|
|
/** How long to keep a specific target entity */
|
|
protected int numTicksToChaseTarget;
|
|
private boolean isLeashed;
|
|
private Entity leashedToEntity;
|
|
private NBTTagCompound field_110170_bx;
|
|
private static final String __OBFID = "CL_00001550";
|
|
|
|
public EntityLiving(World p_i1595_1_)
|
|
{
|
|
super(p_i1595_1_);
|
|
this.tasks = new EntityAITasks(p_i1595_1_ != null && p_i1595_1_.theProfiler != null ? p_i1595_1_.theProfiler : null);
|
|
this.targetTasks = new EntityAITasks(p_i1595_1_ != null && p_i1595_1_.theProfiler != null ? p_i1595_1_.theProfiler : null);
|
|
this.lookHelper = new EntityLookHelper(this);
|
|
this.moveHelper = new EntityMoveHelper(this);
|
|
this.jumpHelper = new EntityJumpHelper(this);
|
|
this.bodyHelper = new EntityBodyHelper(this);
|
|
this.navigator = new PathNavigate(this, p_i1595_1_);
|
|
this.senses = new EntitySenses(this);
|
|
|
|
for (int i = 0; i < this.equipmentDropChances.length; ++i)
|
|
{
|
|
this.equipmentDropChances[i] = 0.085F;
|
|
}
|
|
}
|
|
|
|
protected void applyEntityAttributes()
|
|
{
|
|
super.applyEntityAttributes();
|
|
this.getAttributeMap().registerAttribute(SharedMonsterAttributes.followRange).setBaseValue(16.0D);
|
|
}
|
|
|
|
public EntityLookHelper getLookHelper()
|
|
{
|
|
return this.lookHelper;
|
|
}
|
|
|
|
public EntityMoveHelper getMoveHelper()
|
|
{
|
|
return this.moveHelper;
|
|
}
|
|
|
|
public EntityJumpHelper getJumpHelper()
|
|
{
|
|
return this.jumpHelper;
|
|
}
|
|
|
|
public PathNavigate getNavigator()
|
|
{
|
|
return this.navigator;
|
|
}
|
|
|
|
/**
|
|
* returns the EntitySenses Object for the EntityLiving
|
|
*/
|
|
public EntitySenses getEntitySenses()
|
|
{
|
|
return this.senses;
|
|
}
|
|
|
|
/**
|
|
* Gets the active target the Task system uses for tracking
|
|
*/
|
|
public EntityLivingBase getAttackTarget()
|
|
{
|
|
return this.attackTarget;
|
|
}
|
|
|
|
/**
|
|
* Sets the active target the Task system uses for tracking
|
|
*/
|
|
public void setAttackTarget(EntityLivingBase p_70624_1_)
|
|
{
|
|
this.attackTarget = p_70624_1_;
|
|
ForgeHooks.onLivingSetAttackTarget(this, p_70624_1_);
|
|
}
|
|
|
|
/**
|
|
* Returns true if this entity can attack entities of the specified class.
|
|
*/
|
|
public boolean canAttackClass(Class p_70686_1_)
|
|
{
|
|
return EntityCreeper.class != p_70686_1_ && EntityGhast.class != p_70686_1_;
|
|
}
|
|
|
|
/**
|
|
* This function applies the benefits of growing back wool and faster growing up to the acting entity. (This
|
|
* function is used in the AIEatGrass)
|
|
*/
|
|
public void eatGrassBonus() {}
|
|
|
|
protected void entityInit()
|
|
{
|
|
super.entityInit();
|
|
this.dataWatcher.addObject(11, Byte.valueOf((byte)0));
|
|
this.dataWatcher.addObject(10, "");
|
|
}
|
|
|
|
/**
|
|
* Get number of ticks, at least during which the living entity will be silent.
|
|
*/
|
|
public int getTalkInterval()
|
|
{
|
|
return 80;
|
|
}
|
|
|
|
/**
|
|
* Plays living's sound at its position
|
|
*/
|
|
public void playLivingSound()
|
|
{
|
|
String s = this.getLivingSound();
|
|
|
|
if (s != null)
|
|
{
|
|
this.playSound(s, this.getSoundVolume(), this.getSoundPitch());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets called every tick from main Entity class
|
|
*/
|
|
public void onEntityUpdate()
|
|
{
|
|
super.onEntityUpdate();
|
|
this.worldObj.theProfiler.startSection("mobBaseTick");
|
|
|
|
if (this.isEntityAlive() && this.rand.nextInt(1000) < this.livingSoundTime++)
|
|
{
|
|
this.livingSoundTime = -this.getTalkInterval();
|
|
this.playLivingSound();
|
|
}
|
|
|
|
this.worldObj.theProfiler.endSection();
|
|
}
|
|
|
|
/**
|
|
* Get the experience points the entity currently has.
|
|
*/
|
|
protected int getExperiencePoints(EntityPlayer p_70693_1_)
|
|
{
|
|
if (this.experienceValue > 0)
|
|
{
|
|
int i = this.experienceValue;
|
|
ItemStack[] aitemstack = this.getLastActiveItems();
|
|
|
|
for (int j = 0; j < aitemstack.length; ++j)
|
|
{
|
|
if (aitemstack[j] != null && this.equipmentDropChances[j] <= 1.0F)
|
|
{
|
|
i += 1 + this.rand.nextInt(3);
|
|
}
|
|
}
|
|
|
|
return i;
|
|
}
|
|
else
|
|
{
|
|
return this.experienceValue;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Spawns an explosion particle around the Entity's location
|
|
*/
|
|
public void spawnExplosionParticle()
|
|
{
|
|
for (int i = 0; i < 20; ++i)
|
|
{
|
|
double d0 = this.rand.nextGaussian() * 0.02D;
|
|
double d1 = this.rand.nextGaussian() * 0.02D;
|
|
double d2 = this.rand.nextGaussian() * 0.02D;
|
|
double d3 = 10.0D;
|
|
this.worldObj.spawnParticle("explode", this.posX + (double)(this.rand.nextFloat() * this.width * 2.0F) - (double)this.width - d0 * d3, this.posY + (double)(this.rand.nextFloat() * this.height) - d1 * d3, this.posZ + (double)(this.rand.nextFloat() * this.width * 2.0F) - (double)this.width - d2 * d3, d0, d1, d2);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Called to update the entity's position/logic.
|
|
*/
|
|
public void onUpdate()
|
|
{
|
|
super.onUpdate();
|
|
|
|
if (!this.worldObj.isRemote)
|
|
{
|
|
this.updateLeashedState();
|
|
}
|
|
}
|
|
|
|
protected float func_110146_f(float p_110146_1_, float p_110146_2_)
|
|
{
|
|
if (this.isAIEnabled())
|
|
{
|
|
this.bodyHelper.func_75664_a();
|
|
return p_110146_2_;
|
|
}
|
|
else
|
|
{
|
|
return super.func_110146_f(p_110146_1_, p_110146_2_);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the sound this mob makes while it's alive.
|
|
*/
|
|
protected String getLivingSound()
|
|
{
|
|
return null;
|
|
}
|
|
|
|
protected Item getDropItem()
|
|
{
|
|
return Item.getItemById(0);
|
|
}
|
|
|
|
/**
|
|
* Drop 0-2 items of this living's type. @param par1 - Whether this entity has recently been hit by a player. @param
|
|
* par2 - Level of Looting used to kill this mob.
|
|
*/
|
|
protected void dropFewItems(boolean p_70628_1_, int p_70628_2_)
|
|
{
|
|
Item item = this.getDropItem();
|
|
|
|
if (item != null)
|
|
{
|
|
int j = this.rand.nextInt(3);
|
|
|
|
if (p_70628_2_ > 0)
|
|
{
|
|
j += this.rand.nextInt(p_70628_2_ + 1);
|
|
}
|
|
|
|
for (int k = 0; k < j; ++k)
|
|
{
|
|
this.dropItem(item, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* (abstract) Protected helper method to write subclass entity data to NBT.
|
|
*/
|
|
public void writeEntityToNBT(NBTTagCompound tagCompound)
|
|
{
|
|
super.writeEntityToNBT(tagCompound);
|
|
tagCompound.setBoolean("CanPickUpLoot", this.canPickUpLoot());
|
|
tagCompound.setBoolean("PersistenceRequired", this.persistenceRequired);
|
|
NBTTagList nbttaglist = new NBTTagList();
|
|
NBTTagCompound nbttagcompound1;
|
|
|
|
for (int i = 0; i < this.equipment.length; ++i)
|
|
{
|
|
nbttagcompound1 = new NBTTagCompound();
|
|
|
|
if (this.equipment[i] != null)
|
|
{
|
|
this.equipment[i].writeToNBT(nbttagcompound1);
|
|
}
|
|
|
|
nbttaglist.appendTag(nbttagcompound1);
|
|
}
|
|
|
|
tagCompound.setTag("Equipment", nbttaglist);
|
|
NBTTagList nbttaglist1 = new NBTTagList();
|
|
|
|
for (int j = 0; j < this.equipmentDropChances.length; ++j)
|
|
{
|
|
nbttaglist1.appendTag(new NBTTagFloat(this.equipmentDropChances[j]));
|
|
}
|
|
|
|
tagCompound.setTag("DropChances", nbttaglist1);
|
|
tagCompound.setString("CustomName", this.getCustomNameTag());
|
|
tagCompound.setBoolean("CustomNameVisible", this.getAlwaysRenderNameTag());
|
|
tagCompound.setBoolean("Leashed", this.isLeashed);
|
|
|
|
if (this.leashedToEntity != null)
|
|
{
|
|
nbttagcompound1 = new NBTTagCompound();
|
|
|
|
if (this.leashedToEntity instanceof EntityLivingBase)
|
|
{
|
|
nbttagcompound1.setLong("UUIDMost", this.leashedToEntity.getUniqueID().getMostSignificantBits());
|
|
nbttagcompound1.setLong("UUIDLeast", this.leashedToEntity.getUniqueID().getLeastSignificantBits());
|
|
}
|
|
else if (this.leashedToEntity instanceof EntityHanging)
|
|
{
|
|
EntityHanging entityhanging = (EntityHanging)this.leashedToEntity;
|
|
nbttagcompound1.setInteger("X", entityhanging.field_146063_b);
|
|
nbttagcompound1.setInteger("Y", entityhanging.field_146064_c);
|
|
nbttagcompound1.setInteger("Z", entityhanging.field_146062_d);
|
|
}
|
|
|
|
tagCompound.setTag("Leash", nbttagcompound1);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* (abstract) Protected helper method to read subclass entity data from NBT.
|
|
*/
|
|
public void readEntityFromNBT(NBTTagCompound tagCompund)
|
|
{
|
|
super.readEntityFromNBT(tagCompund);
|
|
this.setCanPickUpLoot(tagCompund.getBoolean("CanPickUpLoot"));
|
|
this.persistenceRequired = tagCompund.getBoolean("PersistenceRequired");
|
|
|
|
if (tagCompund.hasKey("CustomName", 8) && tagCompund.getString("CustomName").length() > 0)
|
|
{
|
|
this.setCustomNameTag(tagCompund.getString("CustomName"));
|
|
}
|
|
|
|
this.setAlwaysRenderNameTag(tagCompund.getBoolean("CustomNameVisible"));
|
|
NBTTagList nbttaglist;
|
|
int i;
|
|
|
|
if (tagCompund.hasKey("Equipment", 9))
|
|
{
|
|
nbttaglist = tagCompund.getTagList("Equipment", 10);
|
|
|
|
for (i = 0; i < this.equipment.length; ++i)
|
|
{
|
|
this.equipment[i] = ItemStack.loadItemStackFromNBT(nbttaglist.getCompoundTagAt(i));
|
|
}
|
|
}
|
|
|
|
if (tagCompund.hasKey("DropChances", 9))
|
|
{
|
|
nbttaglist = tagCompund.getTagList("DropChances", 5);
|
|
|
|
for (i = 0; i < nbttaglist.tagCount(); ++i)
|
|
{
|
|
this.equipmentDropChances[i] = nbttaglist.func_150308_e(i);
|
|
}
|
|
}
|
|
|
|
this.isLeashed = tagCompund.getBoolean("Leashed");
|
|
|
|
if (this.isLeashed && tagCompund.hasKey("Leash", 10))
|
|
{
|
|
this.field_110170_bx = tagCompund.getCompoundTag("Leash");
|
|
}
|
|
}
|
|
|
|
public void setMoveForward(float p_70657_1_)
|
|
{
|
|
this.moveForward = p_70657_1_;
|
|
}
|
|
|
|
/**
|
|
* set the movespeed used for the new AI system
|
|
*/
|
|
public void setAIMoveSpeed(float p_70659_1_)
|
|
{
|
|
super.setAIMoveSpeed(p_70659_1_);
|
|
this.setMoveForward(p_70659_1_);
|
|
}
|
|
|
|
/**
|
|
* 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()
|
|
{
|
|
super.onLivingUpdate();
|
|
this.worldObj.theProfiler.startSection("looting");
|
|
|
|
if (!this.worldObj.isRemote && this.canPickUpLoot() && !this.dead && this.worldObj.getGameRules().getGameRuleBooleanValue("mobGriefing"))
|
|
{
|
|
List list = this.worldObj.getEntitiesWithinAABB(EntityItem.class, this.boundingBox.expand(1.0D, 0.0D, 1.0D));
|
|
Iterator iterator = list.iterator();
|
|
|
|
while (iterator.hasNext())
|
|
{
|
|
EntityItem entityitem = (EntityItem)iterator.next();
|
|
|
|
if (!entityitem.isDead && entityitem.getEntityItem() != null)
|
|
{
|
|
ItemStack itemstack = entityitem.getEntityItem();
|
|
int i = getArmorPosition(itemstack);
|
|
|
|
if (i > -1)
|
|
{
|
|
boolean flag = true;
|
|
ItemStack itemstack1 = this.getEquipmentInSlot(i);
|
|
|
|
if (itemstack1 != null)
|
|
{
|
|
if (i == 0)
|
|
{
|
|
if (itemstack.getItem() instanceof ItemSword && !(itemstack1.getItem() instanceof ItemSword))
|
|
{
|
|
flag = true;
|
|
}
|
|
else if (itemstack.getItem() instanceof ItemSword && itemstack1.getItem() instanceof ItemSword)
|
|
{
|
|
ItemSword itemsword = (ItemSword)itemstack.getItem();
|
|
ItemSword itemsword1 = (ItemSword)itemstack1.getItem();
|
|
|
|
if (itemsword.func_150931_i() == itemsword1.func_150931_i())
|
|
{
|
|
flag = itemstack.getItemDamage() > itemstack1.getItemDamage() || itemstack.hasTagCompound() && !itemstack1.hasTagCompound();
|
|
}
|
|
else
|
|
{
|
|
flag = itemsword.func_150931_i() > itemsword1.func_150931_i();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
flag = false;
|
|
}
|
|
}
|
|
else if (itemstack.getItem() instanceof ItemArmor && !(itemstack1.getItem() instanceof ItemArmor))
|
|
{
|
|
flag = true;
|
|
}
|
|
else if (itemstack.getItem() instanceof ItemArmor && itemstack1.getItem() instanceof ItemArmor)
|
|
{
|
|
ItemArmor itemarmor = (ItemArmor)itemstack.getItem();
|
|
ItemArmor itemarmor1 = (ItemArmor)itemstack1.getItem();
|
|
|
|
if (itemarmor.damageReduceAmount == itemarmor1.damageReduceAmount)
|
|
{
|
|
flag = itemstack.getItemDamage() > itemstack1.getItemDamage() || itemstack.hasTagCompound() && !itemstack1.hasTagCompound();
|
|
}
|
|
else
|
|
{
|
|
flag = itemarmor.damageReduceAmount > itemarmor1.damageReduceAmount;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
flag = false;
|
|
}
|
|
}
|
|
|
|
if (flag)
|
|
{
|
|
if (itemstack1 != null && this.rand.nextFloat() - 0.1F < this.equipmentDropChances[i])
|
|
{
|
|
this.entityDropItem(itemstack1, 0.0F);
|
|
}
|
|
|
|
if (itemstack.getItem() == Items.diamond && entityitem.func_145800_j() != null)
|
|
{
|
|
EntityPlayer entityplayer = this.worldObj.getPlayerEntityByName(entityitem.func_145800_j());
|
|
|
|
if (entityplayer != null)
|
|
{
|
|
entityplayer.triggerAchievement(AchievementList.field_150966_x);
|
|
}
|
|
}
|
|
|
|
this.setCurrentItemOrArmor(i, itemstack);
|
|
this.equipmentDropChances[i] = 2.0F;
|
|
this.persistenceRequired = true;
|
|
this.onItemPickup(entityitem, 1);
|
|
entityitem.setDead();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
this.worldObj.theProfiler.endSection();
|
|
}
|
|
|
|
/**
|
|
* Returns true if the newer Entity AI code should be run
|
|
*/
|
|
protected boolean isAIEnabled()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Determines if an entity can be despawned, used on idle far away entities
|
|
*/
|
|
protected boolean canDespawn()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Makes the entity despawn if requirements are reached
|
|
*/
|
|
protected void despawnEntity()
|
|
{
|
|
Result result = null;
|
|
if (this.persistenceRequired)
|
|
{
|
|
this.entityAge = 0;
|
|
}
|
|
else if ((this.entityAge & 0x1F) == 0x1F && (result = ForgeEventFactory.canEntityDespawn(this)) != Result.DEFAULT)
|
|
{
|
|
if (result == Result.DENY)
|
|
{
|
|
this.entityAge = 0;
|
|
}
|
|
else
|
|
{
|
|
this.setDead();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
EntityPlayer entityplayer = this.worldObj.getClosestPlayerToEntity(this, -1.0D);
|
|
|
|
if (entityplayer != null)
|
|
{
|
|
double d0 = entityplayer.posX - this.posX;
|
|
double d1 = entityplayer.posY - this.posY;
|
|
double d2 = entityplayer.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.nextInt(800) == 0 && d3 > 1024.0D && this.canDespawn())
|
|
{
|
|
this.setDead();
|
|
}
|
|
else if (d3 < 1024.0D)
|
|
{
|
|
this.entityAge = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
protected void updateAITasks()
|
|
{
|
|
++this.entityAge;
|
|
this.worldObj.theProfiler.startSection("checkDespawn");
|
|
this.despawnEntity();
|
|
this.worldObj.theProfiler.endSection();
|
|
this.worldObj.theProfiler.startSection("sensing");
|
|
this.senses.clearSensingCache();
|
|
this.worldObj.theProfiler.endSection();
|
|
this.worldObj.theProfiler.startSection("targetSelector");
|
|
this.targetTasks.onUpdateTasks();
|
|
this.worldObj.theProfiler.endSection();
|
|
this.worldObj.theProfiler.startSection("goalSelector");
|
|
this.tasks.onUpdateTasks();
|
|
this.worldObj.theProfiler.endSection();
|
|
this.worldObj.theProfiler.startSection("navigation");
|
|
this.navigator.onUpdateNavigation();
|
|
this.worldObj.theProfiler.endSection();
|
|
this.worldObj.theProfiler.startSection("mob tick");
|
|
this.updateAITick();
|
|
this.worldObj.theProfiler.endSection();
|
|
this.worldObj.theProfiler.startSection("controls");
|
|
this.worldObj.theProfiler.startSection("move");
|
|
this.moveHelper.onUpdateMoveHelper();
|
|
this.worldObj.theProfiler.endStartSection("look");
|
|
this.lookHelper.onUpdateLook();
|
|
this.worldObj.theProfiler.endStartSection("jump");
|
|
this.jumpHelper.doJump();
|
|
this.worldObj.theProfiler.endSection();
|
|
this.worldObj.theProfiler.endSection();
|
|
}
|
|
|
|
protected void updateEntityActionState()
|
|
{
|
|
super.updateEntityActionState();
|
|
this.moveStrafing = 0.0F;
|
|
this.moveForward = 0.0F;
|
|
this.despawnEntity();
|
|
float f = 8.0F;
|
|
|
|
if (this.rand.nextFloat() < 0.02F)
|
|
{
|
|
EntityPlayer entityplayer = this.worldObj.getClosestPlayerToEntity(this, (double)f);
|
|
|
|
if (entityplayer != null)
|
|
{
|
|
this.currentTarget = entityplayer;
|
|
this.numTicksToChaseTarget = 10 + this.rand.nextInt(20);
|
|
}
|
|
else
|
|
{
|
|
this.randomYawVelocity = (this.rand.nextFloat() - 0.5F) * 20.0F;
|
|
}
|
|
}
|
|
|
|
if (this.currentTarget != null)
|
|
{
|
|
this.faceEntity(this.currentTarget, 10.0F, (float)this.getVerticalFaceSpeed());
|
|
|
|
if (this.numTicksToChaseTarget-- <= 0 || this.currentTarget.isDead || this.currentTarget.getDistanceSqToEntity(this) > (double)(f * f))
|
|
{
|
|
this.currentTarget = null;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (this.rand.nextFloat() < 0.05F)
|
|
{
|
|
this.randomYawVelocity = (this.rand.nextFloat() - 0.5F) * 20.0F;
|
|
}
|
|
|
|
this.rotationYaw += this.randomYawVelocity;
|
|
this.rotationPitch = this.defaultPitch;
|
|
}
|
|
|
|
boolean flag1 = this.isInWater();
|
|
boolean flag = this.handleLavaMovement();
|
|
|
|
if (flag1 || flag)
|
|
{
|
|
this.isJumping = this.rand.nextFloat() < 0.8F;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* The speed it takes to move the entityliving's rotationPitch through the faceEntity method. This is only currently
|
|
* use in wolves.
|
|
*/
|
|
public int getVerticalFaceSpeed()
|
|
{
|
|
return 40;
|
|
}
|
|
|
|
/**
|
|
* Changes pitch and yaw so that the entity calling the function is facing the entity provided as an argument.
|
|
*/
|
|
public void faceEntity(Entity p_70625_1_, float p_70625_2_, float p_70625_3_)
|
|
{
|
|
double d0 = p_70625_1_.posX - this.posX;
|
|
double d2 = p_70625_1_.posZ - this.posZ;
|
|
double d1;
|
|
|
|
if (p_70625_1_ instanceof EntityLivingBase)
|
|
{
|
|
EntityLivingBase entitylivingbase = (EntityLivingBase)p_70625_1_;
|
|
d1 = entitylivingbase.posY + (double)entitylivingbase.getEyeHeight() - (this.posY + (double)this.getEyeHeight());
|
|
}
|
|
else
|
|
{
|
|
d1 = (p_70625_1_.boundingBox.minY + p_70625_1_.boundingBox.maxY) / 2.0D - (this.posY + (double)this.getEyeHeight());
|
|
}
|
|
|
|
double d3 = (double)MathHelper.sqrt_double(d0 * d0 + d2 * d2);
|
|
float f2 = (float)(Math.atan2(d2, d0) * 180.0D / Math.PI) - 90.0F;
|
|
float f3 = (float)(-(Math.atan2(d1, d3) * 180.0D / Math.PI));
|
|
this.rotationPitch = this.updateRotation(this.rotationPitch, f3, p_70625_3_);
|
|
this.rotationYaw = this.updateRotation(this.rotationYaw, f2, p_70625_2_);
|
|
}
|
|
|
|
/**
|
|
* Arguments: current rotation, intended rotation, max increment.
|
|
*/
|
|
private float updateRotation(float p_70663_1_, float p_70663_2_, float p_70663_3_)
|
|
{
|
|
float f3 = MathHelper.wrapAngleTo180_float(p_70663_2_ - p_70663_1_);
|
|
|
|
if (f3 > p_70663_3_)
|
|
{
|
|
f3 = p_70663_3_;
|
|
}
|
|
|
|
if (f3 < -p_70663_3_)
|
|
{
|
|
f3 = -p_70663_3_;
|
|
}
|
|
|
|
return p_70663_1_ + f3;
|
|
}
|
|
|
|
/**
|
|
* Checks if the entity's current position is a valid location to spawn this entity.
|
|
*/
|
|
public boolean getCanSpawnHere()
|
|
{
|
|
return this.worldObj.checkNoEntityCollision(this.boundingBox) && this.worldObj.getCollidingBoundingBoxes(this, this.boundingBox).isEmpty() && !this.worldObj.isAnyLiquid(this.boundingBox);
|
|
}
|
|
|
|
/**
|
|
* Returns render size modifier
|
|
*/
|
|
public float getRenderSizeModifier()
|
|
{
|
|
return 1.0F;
|
|
}
|
|
|
|
/**
|
|
* Will return how many at most can spawn in a chunk at once.
|
|
*/
|
|
public int getMaxSpawnedInChunk()
|
|
{
|
|
return 4;
|
|
}
|
|
|
|
/**
|
|
* The number of iterations PathFinder.getSafePoint will execute before giving up.
|
|
*/
|
|
public int getMaxSafePointTries()
|
|
{
|
|
if (this.getAttackTarget() == null)
|
|
{
|
|
return 3;
|
|
}
|
|
else
|
|
{
|
|
int i = (int)(this.getHealth() - this.getMaxHealth() * 0.33F);
|
|
i -= (3 - this.worldObj.difficultySetting.getDifficultyId()) * 4;
|
|
|
|
if (i < 0)
|
|
{
|
|
i = 0;
|
|
}
|
|
|
|
return i + 3;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the item that this EntityLiving is holding, if any.
|
|
*/
|
|
public ItemStack getHeldItem()
|
|
{
|
|
return this.equipment[0];
|
|
}
|
|
|
|
/**
|
|
* 0: Tool in Hand; 1-4: Armor
|
|
*/
|
|
public ItemStack getEquipmentInSlot(int p_71124_1_)
|
|
{
|
|
return this.equipment[p_71124_1_];
|
|
}
|
|
|
|
public ItemStack func_130225_q(int p_130225_1_)
|
|
{
|
|
return this.equipment[p_130225_1_ + 1];
|
|
}
|
|
|
|
/**
|
|
* Sets the held item, or an armor slot. Slot 0 is held item. Slot 1-4 is armor. Params: Item, slot
|
|
*/
|
|
public void setCurrentItemOrArmor(int slotIn, ItemStack itemStackIn)
|
|
{
|
|
this.equipment[slotIn] = itemStackIn;
|
|
}
|
|
|
|
public ItemStack[] getLastActiveItems()
|
|
{
|
|
return this.equipment;
|
|
}
|
|
|
|
/**
|
|
* Drop the equipment for this entity.
|
|
*/
|
|
protected void dropEquipment(boolean p_82160_1_, int p_82160_2_)
|
|
{
|
|
for (int j = 0; j < this.getLastActiveItems().length; ++j)
|
|
{
|
|
ItemStack itemstack = this.getEquipmentInSlot(j);
|
|
boolean flag1 = this.equipmentDropChances[j] > 1.0F;
|
|
|
|
if (itemstack != null && (p_82160_1_ || flag1) && this.rand.nextFloat() - (float)p_82160_2_ * 0.01F < this.equipmentDropChances[j])
|
|
{
|
|
if (!flag1 && itemstack.isItemStackDamageable())
|
|
{
|
|
int k = Math.max(itemstack.getMaxDamage() - 25, 1);
|
|
int l = itemstack.getMaxDamage() - this.rand.nextInt(this.rand.nextInt(k) + 1);
|
|
|
|
if (l > k)
|
|
{
|
|
l = k;
|
|
}
|
|
|
|
if (l < 1)
|
|
{
|
|
l = 1;
|
|
}
|
|
|
|
itemstack.setItemDamage(l);
|
|
}
|
|
|
|
this.entityDropItem(itemstack, 0.0F);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Makes entity wear random armor based on difficulty
|
|
*/
|
|
protected void addRandomArmor()
|
|
{
|
|
if (this.rand.nextFloat() < 0.15F * this.worldObj.func_147462_b(this.posX, this.posY, this.posZ))
|
|
{
|
|
int i = this.rand.nextInt(2);
|
|
float f = this.worldObj.difficultySetting == EnumDifficulty.HARD ? 0.1F : 0.25F;
|
|
|
|
if (this.rand.nextFloat() < 0.095F)
|
|
{
|
|
++i;
|
|
}
|
|
|
|
if (this.rand.nextFloat() < 0.095F)
|
|
{
|
|
++i;
|
|
}
|
|
|
|
if (this.rand.nextFloat() < 0.095F)
|
|
{
|
|
++i;
|
|
}
|
|
|
|
for (int j = 3; j >= 0; --j)
|
|
{
|
|
ItemStack itemstack = this.func_130225_q(j);
|
|
|
|
if (j < 3 && this.rand.nextFloat() < f)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (itemstack == null)
|
|
{
|
|
Item item = getArmorItemForSlot(j + 1, i);
|
|
|
|
if (item != null)
|
|
{
|
|
this.setCurrentItemOrArmor(j + 1, new ItemStack(item));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public static int getArmorPosition(ItemStack p_82159_0_)
|
|
{
|
|
if (p_82159_0_.getItem() != Item.getItemFromBlock(Blocks.pumpkin) && p_82159_0_.getItem() != Items.skull)
|
|
{
|
|
if (p_82159_0_.getItem() instanceof ItemArmor)
|
|
{
|
|
switch (((ItemArmor)p_82159_0_.getItem()).armorType)
|
|
{
|
|
case 0:
|
|
return 4;
|
|
case 1:
|
|
return 3;
|
|
case 2:
|
|
return 2;
|
|
case 3:
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
return 4;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Params: Armor slot, Item tier
|
|
*/
|
|
public static Item getArmorItemForSlot(int armorSlot, int itemTier)
|
|
{
|
|
switch (armorSlot)
|
|
{
|
|
case 4:
|
|
if (itemTier == 0)
|
|
{
|
|
return Items.leather_helmet;
|
|
}
|
|
else if (itemTier == 1)
|
|
{
|
|
return Items.golden_helmet;
|
|
}
|
|
else if (itemTier == 2)
|
|
{
|
|
return Items.chainmail_helmet;
|
|
}
|
|
else if (itemTier == 3)
|
|
{
|
|
return Items.iron_helmet;
|
|
}
|
|
else if (itemTier == 4)
|
|
{
|
|
return Items.diamond_helmet;
|
|
}
|
|
case 3:
|
|
if (itemTier == 0)
|
|
{
|
|
return Items.leather_chestplate;
|
|
}
|
|
else if (itemTier == 1)
|
|
{
|
|
return Items.golden_chestplate;
|
|
}
|
|
else if (itemTier == 2)
|
|
{
|
|
return Items.chainmail_chestplate;
|
|
}
|
|
else if (itemTier == 3)
|
|
{
|
|
return Items.iron_chestplate;
|
|
}
|
|
else if (itemTier == 4)
|
|
{
|
|
return Items.diamond_chestplate;
|
|
}
|
|
case 2:
|
|
if (itemTier == 0)
|
|
{
|
|
return Items.leather_leggings;
|
|
}
|
|
else if (itemTier == 1)
|
|
{
|
|
return Items.golden_leggings;
|
|
}
|
|
else if (itemTier == 2)
|
|
{
|
|
return Items.chainmail_leggings;
|
|
}
|
|
else if (itemTier == 3)
|
|
{
|
|
return Items.iron_leggings;
|
|
}
|
|
else if (itemTier == 4)
|
|
{
|
|
return Items.diamond_leggings;
|
|
}
|
|
case 1:
|
|
if (itemTier == 0)
|
|
{
|
|
return Items.leather_boots;
|
|
}
|
|
else if (itemTier == 1)
|
|
{
|
|
return Items.golden_boots;
|
|
}
|
|
else if (itemTier == 2)
|
|
{
|
|
return Items.chainmail_boots;
|
|
}
|
|
else if (itemTier == 3)
|
|
{
|
|
return Items.iron_boots;
|
|
}
|
|
else if (itemTier == 4)
|
|
{
|
|
return Items.diamond_boots;
|
|
}
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Enchants the entity's armor and held item based on difficulty
|
|
*/
|
|
protected void enchantEquipment()
|
|
{
|
|
float f = this.worldObj.func_147462_b(this.posX, this.posY, this.posZ);
|
|
|
|
if (this.getHeldItem() != null && this.rand.nextFloat() < 0.25F * f)
|
|
{
|
|
EnchantmentHelper.addRandomEnchantment(this.rand, this.getHeldItem(), (int)(5.0F + f * (float)this.rand.nextInt(18)));
|
|
}
|
|
|
|
for (int i = 0; i < 4; ++i)
|
|
{
|
|
ItemStack itemstack = this.func_130225_q(i);
|
|
|
|
if (itemstack != null && this.rand.nextFloat() < 0.5F * f)
|
|
{
|
|
EnchantmentHelper.addRandomEnchantment(this.rand, itemstack, (int)(5.0F + f * (float)this.rand.nextInt(18)));
|
|
}
|
|
}
|
|
}
|
|
|
|
public IEntityLivingData onSpawnWithEgg(IEntityLivingData p_110161_1_)
|
|
{
|
|
this.getEntityAttribute(SharedMonsterAttributes.followRange).applyModifier(new AttributeModifier("Random spawn bonus", this.rand.nextGaussian() * 0.05D, 1));
|
|
return p_110161_1_;
|
|
}
|
|
|
|
/**
|
|
* returns true if all the conditions for steering the entity are met. For pigs, this is true if it is being ridden
|
|
* by a player and the player is holding a carrot-on-a-stick
|
|
*/
|
|
public boolean canBeSteered()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Gets the name of this command sender (usually username, but possibly "Rcon")
|
|
*/
|
|
public String getCommandSenderName()
|
|
{
|
|
return this.hasCustomNameTag() ? this.getCustomNameTag() : super.getCommandSenderName();
|
|
}
|
|
|
|
public void func_110163_bv()
|
|
{
|
|
this.persistenceRequired = true;
|
|
}
|
|
|
|
public void setCustomNameTag(String p_94058_1_)
|
|
{
|
|
this.dataWatcher.updateObject(10, p_94058_1_);
|
|
}
|
|
|
|
public String getCustomNameTag()
|
|
{
|
|
return this.dataWatcher.getWatchableObjectString(10);
|
|
}
|
|
|
|
public boolean hasCustomNameTag()
|
|
{
|
|
return this.dataWatcher.getWatchableObjectString(10).length() > 0;
|
|
}
|
|
|
|
public void setAlwaysRenderNameTag(boolean p_94061_1_)
|
|
{
|
|
this.dataWatcher.updateObject(11, Byte.valueOf((byte)(p_94061_1_ ? 1 : 0)));
|
|
}
|
|
|
|
public boolean getAlwaysRenderNameTag()
|
|
{
|
|
return this.dataWatcher.getWatchableObjectByte(11) == 1;
|
|
}
|
|
|
|
@SideOnly(Side.CLIENT)
|
|
public boolean getAlwaysRenderNameTagForRender()
|
|
{
|
|
return this.getAlwaysRenderNameTag();
|
|
}
|
|
|
|
public void setEquipmentDropChance(int p_96120_1_, float p_96120_2_)
|
|
{
|
|
this.equipmentDropChances[p_96120_1_] = p_96120_2_;
|
|
}
|
|
|
|
public boolean canPickUpLoot()
|
|
{
|
|
return this.canPickUpLoot;
|
|
}
|
|
|
|
public void setCanPickUpLoot(boolean p_98053_1_)
|
|
{
|
|
this.canPickUpLoot = p_98053_1_;
|
|
}
|
|
|
|
public boolean isNoDespawnRequired()
|
|
{
|
|
return this.persistenceRequired;
|
|
}
|
|
|
|
/**
|
|
* First layer of player interaction
|
|
*/
|
|
public final boolean interactFirst(EntityPlayer player)
|
|
{
|
|
if (this.getLeashed() && this.getLeashedToEntity() == player)
|
|
{
|
|
this.clearLeashed(true, !player.capabilities.isCreativeMode);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
ItemStack itemstack = player.inventory.getCurrentItem();
|
|
|
|
if (itemstack != null && itemstack.getItem() == Items.lead && this.allowLeashing())
|
|
{
|
|
if (!(this instanceof EntityTameable) || !((EntityTameable)this).isTamed())
|
|
{
|
|
this.setLeashedToEntity(player, true);
|
|
--itemstack.stackSize;
|
|
return true;
|
|
}
|
|
|
|
if (((EntityTameable)this).func_152114_e(player))
|
|
{
|
|
this.setLeashedToEntity(player, true);
|
|
--itemstack.stackSize;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return this.interact(player) ? true : super.interactFirst(player);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Called when a player interacts with a mob. e.g. gets milk from a cow, gets into the saddle on a pig.
|
|
*/
|
|
protected boolean interact(EntityPlayer p_70085_1_)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Applies logic related to leashes, for example dragging the entity or breaking the leash.
|
|
*/
|
|
protected void updateLeashedState()
|
|
{
|
|
if (this.field_110170_bx != null)
|
|
{
|
|
this.recreateLeash();
|
|
}
|
|
|
|
if (this.isLeashed)
|
|
{
|
|
if (this.leashedToEntity == null || this.leashedToEntity.isDead)
|
|
{
|
|
this.clearLeashed(true, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Removes the leash from this entity. Second parameter tells whether to send a packet to surrounding players.
|
|
*/
|
|
public void clearLeashed(boolean p_110160_1_, boolean p_110160_2_)
|
|
{
|
|
if (this.isLeashed)
|
|
{
|
|
this.isLeashed = false;
|
|
this.leashedToEntity = null;
|
|
|
|
if (!this.worldObj.isRemote && p_110160_2_)
|
|
{
|
|
this.dropItem(Items.lead, 1);
|
|
}
|
|
|
|
if (!this.worldObj.isRemote && p_110160_1_ && this.worldObj instanceof WorldServer)
|
|
{
|
|
((WorldServer)this.worldObj).getEntityTracker().func_151247_a(this, new S1BPacketEntityAttach(1, this, (Entity)null));
|
|
}
|
|
}
|
|
}
|
|
|
|
public boolean allowLeashing()
|
|
{
|
|
return !this.getLeashed() && !(this instanceof IMob);
|
|
}
|
|
|
|
public boolean getLeashed()
|
|
{
|
|
return this.isLeashed;
|
|
}
|
|
|
|
public Entity getLeashedToEntity()
|
|
{
|
|
return this.leashedToEntity;
|
|
}
|
|
|
|
/**
|
|
* Sets the entity to be leashed to.
|
|
* Args:
|
|
* @param par1Entity: The entity to be tethered to.
|
|
* @param par2: Whether to send an attaching notification packet to surrounding players.
|
|
*/
|
|
public void setLeashedToEntity(Entity entityIn, boolean sendAttachNotification)
|
|
{
|
|
this.isLeashed = true;
|
|
this.leashedToEntity = entityIn;
|
|
|
|
if (!this.worldObj.isRemote && sendAttachNotification && this.worldObj instanceof WorldServer)
|
|
{
|
|
((WorldServer)this.worldObj).getEntityTracker().func_151247_a(this, new S1BPacketEntityAttach(1, this, this.leashedToEntity));
|
|
}
|
|
}
|
|
|
|
private void recreateLeash()
|
|
{
|
|
if (this.isLeashed && this.field_110170_bx != null)
|
|
{
|
|
if (this.field_110170_bx.hasKey("UUIDMost", 4) && this.field_110170_bx.hasKey("UUIDLeast", 4))
|
|
{
|
|
UUID uuid = new UUID(this.field_110170_bx.getLong("UUIDMost"), this.field_110170_bx.getLong("UUIDLeast"));
|
|
List list = this.worldObj.getEntitiesWithinAABB(EntityLivingBase.class, this.boundingBox.expand(10.0D, 10.0D, 10.0D));
|
|
Iterator iterator = list.iterator();
|
|
|
|
while (iterator.hasNext())
|
|
{
|
|
EntityLivingBase entitylivingbase = (EntityLivingBase)iterator.next();
|
|
|
|
if (entitylivingbase.getUniqueID().equals(uuid))
|
|
{
|
|
this.leashedToEntity = entitylivingbase;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (this.field_110170_bx.hasKey("X", 99) && this.field_110170_bx.hasKey("Y", 99) && this.field_110170_bx.hasKey("Z", 99))
|
|
{
|
|
int i = this.field_110170_bx.getInteger("X");
|
|
int j = this.field_110170_bx.getInteger("Y");
|
|
int k = this.field_110170_bx.getInteger("Z");
|
|
EntityLeashKnot entityleashknot = EntityLeashKnot.getKnotForBlock(this.worldObj, i, j, k);
|
|
|
|
if (entityleashknot == null)
|
|
{
|
|
entityleashknot = EntityLeashKnot.func_110129_a(this.worldObj, i, j, k);
|
|
}
|
|
|
|
this.leashedToEntity = entityleashknot;
|
|
}
|
|
else
|
|
{
|
|
this.clearLeashed(false, true);
|
|
}
|
|
}
|
|
|
|
this.field_110170_bx = null;
|
|
}
|
|
} |