package net.minecraft.entity; import com.google.common.collect.Maps; import java.util.Arrays; import java.util.Map; import java.util.Random; import java.util.UUID; import javax.annotation.Nullable; import net.minecraft.block.state.IBlockState; 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.EntityBoat; import net.minecraft.entity.item.EntityItem; import net.minecraft.entity.monster.EntityGhast; import net.minecraft.entity.monster.IMob; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.Blocks; import net.minecraft.init.Items; import net.minecraft.inventory.EntityEquipmentSlot; import net.minecraft.item.Item; import net.minecraft.item.ItemArmor; import net.minecraft.item.ItemBow; 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.datasync.DataParameter; import net.minecraft.network.datasync.DataSerializers; import net.minecraft.network.datasync.EntityDataManager; import net.minecraft.network.play.server.SPacketEntityAttach; import net.minecraft.pathfinding.PathNavigate; import net.minecraft.pathfinding.PathNavigateGround; import net.minecraft.pathfinding.PathNodeType; import net.minecraft.util.DamageSource; import net.minecraft.util.EnumHand; import net.minecraft.util.EnumHandSide; import net.minecraft.util.EnumParticleTypes; import net.minecraft.util.NonNullList; import net.minecraft.util.ResourceLocation; import net.minecraft.util.SoundEvent; import net.minecraft.util.datafix.DataFixer; import net.minecraft.util.datafix.FixTypes; import net.minecraft.util.datafix.walkers.ItemStackDataLists; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; import net.minecraft.world.DifficultyInstance; import net.minecraft.world.EnumDifficulty; import net.minecraft.world.World; import net.minecraft.world.WorldServer; import net.minecraft.world.storage.loot.LootContext; import net.minecraft.world.storage.loot.LootTable; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; public abstract class EntityLiving extends EntityLivingBase { /** First bit used for AI disabling, second bit used for marking entity as left handed */ private static final DataParameter AI_FLAGS = EntityDataManager.createKey(EntityLiving.class, DataSerializers.BYTE); /** Number of ticks since this EntityLiving last produced its sound */ public int livingSoundTime; /** The experience points the Entity gives. */ protected int experienceValue; private final EntityLookHelper lookHelper; protected EntityMoveHelper moveHelper; /** Entity jumping helper */ protected EntityJumpHelper jumpHelper; private final EntityBodyHelper bodyHelper; protected PathNavigate navigator; /** Active AI tasks (moving, looking, attack the target selected by {@link #targetTasks}, etc.) */ public final EntityAITasks tasks; /** (Usually one-shot) tasks used to select an attack target */ public final EntityAITasks targetTasks; /** The active target the Task system uses for tracking */ private EntityLivingBase attackTarget; private final EntitySenses senses; private final NonNullList inventoryHands = NonNullList.withSize(2, ItemStack.EMPTY); /** Chances for equipment in hands dropping when this entity dies. */ protected float[] inventoryHandsDropChances = new float[2]; private final NonNullList inventoryArmor = NonNullList.withSize(4, ItemStack.EMPTY); /** Chances for armor dropping when this entity dies. */ protected float[] inventoryArmorDropChances = new float[4]; /** Whether this entity can pick up items from the ground. */ private boolean canPickUpLoot; /** Whether this entity should NOT despawn. */ private boolean persistenceRequired; private final Map mapPathPriority = Maps.newEnumMap(PathNodeType.class); private ResourceLocation deathLootTable; private long deathLootTableSeed; private boolean isLeashed; private Entity leashHolder; private NBTTagCompound leashNBTTag; public EntityLiving(World worldIn) { super(worldIn); this.tasks = new EntityAITasks(worldIn != null && worldIn.profiler != null ? worldIn.profiler : null); this.targetTasks = new EntityAITasks(worldIn != null && worldIn.profiler != null ? worldIn.profiler : null); this.lookHelper = new EntityLookHelper(this); this.moveHelper = new EntityMoveHelper(this); this.jumpHelper = new EntityJumpHelper(this); this.bodyHelper = this.createBodyHelper(); this.navigator = this.createNavigator(worldIn); this.senses = new EntitySenses(this); Arrays.fill(this.inventoryArmorDropChances, 0.085F); Arrays.fill(this.inventoryHandsDropChances, 0.085F); if (worldIn != null && !worldIn.isRemote) { this.initEntityAI(); } } protected void initEntityAI() { } protected void applyEntityAttributes() { super.applyEntityAttributes(); this.getAttributeMap().registerAttribute(SharedMonsterAttributes.FOLLOW_RANGE).setBaseValue(16.0D); } /** * Returns new PathNavigateGround instance */ protected PathNavigate createNavigator(World worldIn) { return new PathNavigateGround(this, worldIn); } public float getPathPriority(PathNodeType nodeType) { Float f = this.mapPathPriority.get(nodeType); return f == null ? nodeType.getPriority() : f.floatValue(); } public void setPathPriority(PathNodeType nodeType, float priority) { this.mapPathPriority.put(nodeType, Float.valueOf(priority)); } protected EntityBodyHelper createBodyHelper() { return new EntityBodyHelper(this); } 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 */ @Nullable public EntityLivingBase getAttackTarget() { return this.attackTarget; } /** * Sets the active target the Task system uses for tracking */ public void setAttackTarget(@Nullable EntityLivingBase entitylivingbaseIn) { this.attackTarget = entitylivingbaseIn; net.minecraftforge.common.ForgeHooks.onLivingSetAttackTarget(this, entitylivingbaseIn); } /** * Returns true if this entity can attack entities of the specified class. */ public boolean canAttackClass(Class cls) { return cls != EntityGhast.class; } /** * 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.dataManager.register(AI_FLAGS, Byte.valueOf((byte)0)); } /** * 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() { SoundEvent soundevent = this.getAmbientSound(); if (soundevent != null) { this.playSound(soundevent, this.getSoundVolume(), this.getSoundPitch()); } } /** * Gets called every tick from main Entity class */ public void onEntityUpdate() { super.onEntityUpdate(); this.world.profiler.startSection("mobBaseTick"); if (this.isEntityAlive() && this.rand.nextInt(1000) < this.livingSoundTime++) { this.applyEntityAI(); this.playLivingSound(); } this.world.profiler.endSection(); } protected void playHurtSound(DamageSource source) { this.applyEntityAI(); super.playHurtSound(source); } private void applyEntityAI() { this.livingSoundTime = -this.getTalkInterval(); } /** * Get the experience points the entity currently has. */ protected int getExperiencePoints(EntityPlayer player) { if (this.experienceValue > 0) { int i = this.experienceValue; for (int j = 0; j < this.inventoryArmor.size(); ++j) { if (!((ItemStack)this.inventoryArmor.get(j)).isEmpty() && this.inventoryArmorDropChances[j] <= 1.0F) { i += 1 + this.rand.nextInt(3); } } for (int k = 0; k < this.inventoryHands.size(); ++k) { if (!((ItemStack)this.inventoryHands.get(k)).isEmpty() && this.inventoryHandsDropChances[k] <= 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() { if (this.world.isRemote) { 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.world.spawnParticle(EnumParticleTypes.EXPLOSION_NORMAL, this.posX + (double)(this.rand.nextFloat() * this.width * 2.0F) - (double)this.width - d0 * 10.0D, this.posY + (double)(this.rand.nextFloat() * this.height) - d1 * 10.0D, this.posZ + (double)(this.rand.nextFloat() * this.width * 2.0F) - (double)this.width - d2 * 10.0D, d0, d1, d2); } } else { this.world.setEntityState(this, (byte)20); } } /** * Handler for {@link World#setEntityState} */ @SideOnly(Side.CLIENT) public void handleStatusUpdate(byte id) { if (id == 20) { this.spawnExplosionParticle(); } else { super.handleStatusUpdate(id); } } /** * Called to update the entity's position/logic. */ public void onUpdate() { super.onUpdate(); if (!this.world.isRemote) { this.updateLeashedState(); if (this.ticksExisted % 5 == 0) { boolean flag = !(this.getControllingPassenger() instanceof EntityLiving); boolean flag1 = !(this.getRidingEntity() instanceof EntityBoat); this.tasks.setControlFlag(1, flag); this.tasks.setControlFlag(4, flag && flag1); this.tasks.setControlFlag(2, flag); } } } protected float updateDistance(float p_110146_1_, float p_110146_2_) { this.bodyHelper.updateRenderAngles(); return p_110146_2_; } @Nullable protected SoundEvent getAmbientSound() { return null; } @Nullable protected Item getDropItem() { return null; } /** * Drop 0-2 items of this living's type */ protected void dropFewItems(boolean wasRecentlyHit, int lootingModifier) { Item item = this.getDropItem(); if (item != null) { int i = this.rand.nextInt(3); if (lootingModifier > 0) { i += this.rand.nextInt(lootingModifier + 1); } for (int j = 0; j < i; ++j) { this.dropItem(item, 1); } } } public static void registerFixesMob(DataFixer fixer, Class name) { fixer.registerWalker(FixTypes.ENTITY, new ItemStackDataLists(name, new String[] {"ArmorItems", "HandItems"})); } /** * (abstract) Protected helper method to write subclass entity data to NBT. */ public void writeEntityToNBT(NBTTagCompound compound) { super.writeEntityToNBT(compound); compound.setBoolean("CanPickUpLoot", this.canPickUpLoot()); compound.setBoolean("PersistenceRequired", this.persistenceRequired); NBTTagList nbttaglist = new NBTTagList(); for (ItemStack itemstack : this.inventoryArmor) { NBTTagCompound nbttagcompound = new NBTTagCompound(); if (!itemstack.isEmpty()) { itemstack.writeToNBT(nbttagcompound); } nbttaglist.appendTag(nbttagcompound); } compound.setTag("ArmorItems", nbttaglist); NBTTagList nbttaglist1 = new NBTTagList(); for (ItemStack itemstack1 : this.inventoryHands) { NBTTagCompound nbttagcompound1 = new NBTTagCompound(); if (!itemstack1.isEmpty()) { itemstack1.writeToNBT(nbttagcompound1); } nbttaglist1.appendTag(nbttagcompound1); } compound.setTag("HandItems", nbttaglist1); NBTTagList nbttaglist2 = new NBTTagList(); for (float f : this.inventoryArmorDropChances) { nbttaglist2.appendTag(new NBTTagFloat(f)); } compound.setTag("ArmorDropChances", nbttaglist2); NBTTagList nbttaglist3 = new NBTTagList(); for (float f1 : this.inventoryHandsDropChances) { nbttaglist3.appendTag(new NBTTagFloat(f1)); } compound.setTag("HandDropChances", nbttaglist3); compound.setBoolean("Leashed", this.isLeashed); if (this.leashHolder != null) { NBTTagCompound nbttagcompound2 = new NBTTagCompound(); if (this.leashHolder instanceof EntityLivingBase) { UUID uuid = this.leashHolder.getUniqueID(); nbttagcompound2.setUniqueId("UUID", uuid); } else if (this.leashHolder instanceof EntityHanging) { BlockPos blockpos = ((EntityHanging)this.leashHolder).getHangingPosition(); nbttagcompound2.setInteger("X", blockpos.getX()); nbttagcompound2.setInteger("Y", blockpos.getY()); nbttagcompound2.setInteger("Z", blockpos.getZ()); } compound.setTag("Leash", nbttagcompound2); } compound.setBoolean("LeftHanded", this.isLeftHanded()); if (this.deathLootTable != null) { compound.setString("DeathLootTable", this.deathLootTable.toString()); if (this.deathLootTableSeed != 0L) { compound.setLong("DeathLootTableSeed", this.deathLootTableSeed); } } if (this.isAIDisabled()) { compound.setBoolean("NoAI", this.isAIDisabled()); } } /** * (abstract) Protected helper method to read subclass entity data from NBT. */ public void readEntityFromNBT(NBTTagCompound compound) { super.readEntityFromNBT(compound); if (compound.hasKey("CanPickUpLoot", 1)) { this.setCanPickUpLoot(compound.getBoolean("CanPickUpLoot")); } this.persistenceRequired = compound.getBoolean("PersistenceRequired"); if (compound.hasKey("ArmorItems", 9)) { NBTTagList nbttaglist = compound.getTagList("ArmorItems", 10); for (int i = 0; i < this.inventoryArmor.size(); ++i) { this.inventoryArmor.set(i, new ItemStack(nbttaglist.getCompoundTagAt(i))); } } if (compound.hasKey("HandItems", 9)) { NBTTagList nbttaglist1 = compound.getTagList("HandItems", 10); for (int j = 0; j < this.inventoryHands.size(); ++j) { this.inventoryHands.set(j, new ItemStack(nbttaglist1.getCompoundTagAt(j))); } } if (compound.hasKey("ArmorDropChances", 9)) { NBTTagList nbttaglist2 = compound.getTagList("ArmorDropChances", 5); for (int k = 0; k < nbttaglist2.tagCount(); ++k) { this.inventoryArmorDropChances[k] = nbttaglist2.getFloatAt(k); } } if (compound.hasKey("HandDropChances", 9)) { NBTTagList nbttaglist3 = compound.getTagList("HandDropChances", 5); for (int l = 0; l < nbttaglist3.tagCount(); ++l) { this.inventoryHandsDropChances[l] = nbttaglist3.getFloatAt(l); } } this.isLeashed = compound.getBoolean("Leashed"); if (this.isLeashed && compound.hasKey("Leash", 10)) { this.leashNBTTag = compound.getCompoundTag("Leash"); } this.setLeftHanded(compound.getBoolean("LeftHanded")); if (compound.hasKey("DeathLootTable", 8)) { this.deathLootTable = new ResourceLocation(compound.getString("DeathLootTable")); this.deathLootTableSeed = compound.getLong("DeathLootTableSeed"); } this.setNoAI(compound.getBoolean("NoAI")); } @Nullable protected ResourceLocation getLootTable() { return null; } /** * drops the loot of this entity upon death */ protected void dropLoot(boolean wasRecentlyHit, int lootingModifier, DamageSource source) { ResourceLocation resourcelocation = this.deathLootTable; if (resourcelocation == null) { resourcelocation = this.getLootTable(); } if (resourcelocation != null) { LootTable loottable = this.world.getLootTableManager().getLootTableFromLocation(resourcelocation); this.deathLootTable = null; LootContext.Builder lootcontext$builder = (new LootContext.Builder((WorldServer)this.world)).withLootedEntity(this).withDamageSource(source); if (wasRecentlyHit && this.attackingPlayer != null) { lootcontext$builder = lootcontext$builder.withPlayer(this.attackingPlayer).withLuck(this.attackingPlayer.getLuck()); } for (ItemStack itemstack : loottable.generateLootForPools(this.deathLootTableSeed == 0L ? this.rand : new Random(this.deathLootTableSeed), lootcontext$builder.build())) { this.entityDropItem(itemstack, 0.0F); } this.dropEquipment(wasRecentlyHit, lootingModifier); } else { super.dropLoot(wasRecentlyHit, lootingModifier, source); } } public void setMoveForward(float amount) { this.moveForward = amount; } public void setMoveVertical(float amount) { this.moveVertical = amount; } public void setMoveStrafing(float amount) { this.moveStrafing = amount; } /** * set the movespeed used for the new AI system */ public void setAIMoveSpeed(float speedIn) { super.setAIMoveSpeed(speedIn); this.setMoveForward(speedIn); } /** * 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.world.profiler.startSection("looting"); if (!this.world.isRemote && this.canPickUpLoot() && !this.dead && net.minecraftforge.event.ForgeEventFactory.getMobGriefingEvent(this.world, this)) { for (EntityItem entityitem : this.world.getEntitiesWithinAABB(EntityItem.class, this.getEntityBoundingBox().grow(1.0D, 0.0D, 1.0D))) { if (!entityitem.isDead && !entityitem.getItem().isEmpty() && !entityitem.cannotPickup()) { this.updateEquipmentIfNeeded(entityitem); } } } this.world.profiler.endSection(); } /** * Tests if this entity should pickup a weapon or an armor. Entity drops current weapon or armor if the new one is * better. */ protected void updateEquipmentIfNeeded(EntityItem itemEntity) { ItemStack itemstack = itemEntity.getItem(); EntityEquipmentSlot entityequipmentslot = getSlotForItemStack(itemstack); boolean flag = true; ItemStack itemstack1 = this.getItemStackFromSlot(entityequipmentslot); if (!itemstack1.isEmpty()) { if (entityequipmentslot.getSlotType() == EntityEquipmentSlot.Type.HAND) { 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.getAttackDamage() == itemsword1.getAttackDamage()) { flag = itemstack.getMetadata() > itemstack1.getMetadata() || itemstack.hasTagCompound() && !itemstack1.hasTagCompound(); } else { flag = itemsword.getAttackDamage() > itemsword1.getAttackDamage(); } } else if (itemstack.getItem() instanceof ItemBow && itemstack1.getItem() instanceof ItemBow) { flag = itemstack.hasTagCompound() && !itemstack1.hasTagCompound(); } 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 && !EnchantmentHelper.hasBindingCurse(itemstack1)) { ItemArmor itemarmor = (ItemArmor)itemstack.getItem(); ItemArmor itemarmor1 = (ItemArmor)itemstack1.getItem(); if (itemarmor.damageReduceAmount == itemarmor1.damageReduceAmount) { flag = itemstack.getMetadata() > itemstack1.getMetadata() || itemstack.hasTagCompound() && !itemstack1.hasTagCompound(); } else { flag = itemarmor.damageReduceAmount > itemarmor1.damageReduceAmount; } } else { flag = false; } } if (flag && this.canEquipItem(itemstack)) { double d0; switch (entityequipmentslot.getSlotType()) { case HAND: d0 = (double)this.inventoryHandsDropChances[entityequipmentslot.getIndex()]; break; case ARMOR: d0 = (double)this.inventoryArmorDropChances[entityequipmentslot.getIndex()]; break; default: d0 = 0.0D; } if (!itemstack1.isEmpty() && (double)(this.rand.nextFloat() - 0.1F) < d0) { this.entityDropItem(itemstack1, 0.0F); } this.setItemStackToSlot(entityequipmentslot, itemstack); switch (entityequipmentslot.getSlotType()) { case HAND: this.inventoryHandsDropChances[entityequipmentslot.getIndex()] = 2.0F; break; case ARMOR: this.inventoryArmorDropChances[entityequipmentslot.getIndex()] = 2.0F; } this.persistenceRequired = true; this.onItemPickup(itemEntity, itemstack.getCount()); itemEntity.setDead(); } } protected boolean canEquipItem(ItemStack stack) { return true; } /** * 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() { net.minecraftforge.fml.common.eventhandler.Event.Result result = null; if (this.persistenceRequired) { this.idleTime = 0; } else if ((this.idleTime & 0x1F) == 0x1F && (result = net.minecraftforge.event.ForgeEventFactory.canEntityDespawn(this)) != net.minecraftforge.fml.common.eventhandler.Event.Result.DEFAULT) { if (result == net.minecraftforge.fml.common.eventhandler.Event.Result.DENY) { this.idleTime = 0; } else { this.setDead(); } } else { Entity entity = this.world.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.idleTime > 600 && this.rand.nextInt(800) == 0 && d3 > 1024.0D && this.canDespawn()) { this.setDead(); } else if (d3 < 1024.0D) { this.idleTime = 0; } } } } protected final void updateEntityActionState() { ++this.idleTime; this.world.profiler.startSection("checkDespawn"); this.despawnEntity(); this.world.profiler.endSection(); this.world.profiler.startSection("sensing"); this.senses.clearSensingCache(); this.world.profiler.endSection(); this.world.profiler.startSection("targetSelector"); this.targetTasks.onUpdateTasks(); this.world.profiler.endSection(); this.world.profiler.startSection("goalSelector"); this.tasks.onUpdateTasks(); this.world.profiler.endSection(); this.world.profiler.startSection("navigation"); this.navigator.onUpdateNavigation(); this.world.profiler.endSection(); this.world.profiler.startSection("mob tick"); this.updateAITasks(); this.world.profiler.endSection(); if (this.isRiding() && this.getRidingEntity() instanceof EntityLiving) { EntityLiving entityliving = (EntityLiving)this.getRidingEntity(); entityliving.getNavigator().setPath(this.getNavigator().getPath(), 1.5D); entityliving.getMoveHelper().read(this.getMoveHelper()); } this.world.profiler.startSection("controls"); this.world.profiler.startSection("move"); this.moveHelper.onUpdateMoveHelper(); this.world.profiler.endStartSection("look"); this.lookHelper.onUpdateLook(); this.world.profiler.endStartSection("jump"); this.jumpHelper.doJump(); this.world.profiler.endSection(); this.world.profiler.endSection(); } protected void updateAITasks() { } /** * 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; } public int getHorizontalFaceSpeed() { return 10; } /** * Changes pitch and yaw so that the entity calling the function is facing the entity provided as an argument. */ public void faceEntity(Entity entityIn, float maxYawIncrease, float maxPitchIncrease) { double d0 = entityIn.posX - this.posX; double d2 = entityIn.posZ - this.posZ; double d1; if (entityIn instanceof EntityLivingBase) { EntityLivingBase entitylivingbase = (EntityLivingBase)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)MathHelper.sqrt(d0 * d0 + d2 * d2); float f = (float)(MathHelper.atan2(d2, d0) * (180D / Math.PI)) - 90.0F; float f1 = (float)(-(MathHelper.atan2(d1, d3) * (180D / Math.PI))); this.rotationPitch = this.updateRotation(this.rotationPitch, f1, maxPitchIncrease); this.rotationYaw = this.updateRotation(this.rotationYaw, f, maxYawIncrease); } /** * Arguments: current rotation, intended rotation, max increment. */ private float updateRotation(float angle, float targetAngle, float maxIncrease) { float f = MathHelper.wrapDegrees(targetAngle - angle); if (f > maxIncrease) { f = maxIncrease; } if (f < -maxIncrease) { f = -maxIncrease; } return angle + f; } /** * Checks if the entity's current position is a valid location to spawn this entity. */ public boolean getCanSpawnHere() { IBlockState iblockstate = this.world.getBlockState((new BlockPos(this)).down()); return iblockstate.canEntitySpawn(this); } /** * Checks that the entity is not colliding with any blocks / liquids */ public boolean isNotColliding() { return !this.world.containsAnyLiquid(this.getEntityBoundingBox()) && this.world.getCollisionBoxes(this, this.getEntityBoundingBox()).isEmpty() && this.world.checkNoEntityCollision(this.getEntityBoundingBox(), this); } /** * 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 maximum height from where the entity is alowed to jump (used in pathfinder) */ public int getMaxFallHeight() { if (this.getAttackTarget() == null) { return 3; } else { int i = (int)(this.getHealth() - this.getMaxHealth() * 0.33F); i = i - (3 - this.world.getDifficulty().getDifficultyId()) * 4; if (i < 0) { i = 0; } return i + 3; } } public Iterable getHeldEquipment() { return this.inventoryHands; } public Iterable getArmorInventoryList() { return this.inventoryArmor; } public ItemStack getItemStackFromSlot(EntityEquipmentSlot slotIn) { switch (slotIn.getSlotType()) { case HAND: return this.inventoryHands.get(slotIn.getIndex()); case ARMOR: return this.inventoryArmor.get(slotIn.getIndex()); default: return ItemStack.EMPTY; } } public void setItemStackToSlot(EntityEquipmentSlot slotIn, ItemStack stack) { switch (slotIn.getSlotType()) { case HAND: this.inventoryHands.set(slotIn.getIndex(), stack); break; case ARMOR: this.inventoryArmor.set(slotIn.getIndex(), stack); } } /** * Drop the equipment for this entity. */ protected void dropEquipment(boolean wasRecentlyHit, int lootingModifier) { for (EntityEquipmentSlot entityequipmentslot : EntityEquipmentSlot.values()) { ItemStack itemstack = this.getItemStackFromSlot(entityequipmentslot); double d0; switch (entityequipmentslot.getSlotType()) { case HAND: d0 = (double)this.inventoryHandsDropChances[entityequipmentslot.getIndex()]; break; case ARMOR: d0 = (double)this.inventoryArmorDropChances[entityequipmentslot.getIndex()]; break; default: d0 = 0.0D; } boolean flag = d0 > 1.0D; if (!itemstack.isEmpty() && !EnchantmentHelper.hasVanishingCurse(itemstack) && (wasRecentlyHit || flag) && (double)(this.rand.nextFloat() - (float)lootingModifier * 0.01F) < d0) { if (!flag && itemstack.isItemStackDamageable()) { itemstack.setItemDamage(itemstack.getMaxDamage() - this.rand.nextInt(1 + this.rand.nextInt(Math.max(itemstack.getMaxDamage() - 3, 1)))); } this.entityDropItem(itemstack, 0.0F); } } } /** * Gives armor or weapon for entity based on given DifficultyInstance */ protected void setEquipmentBasedOnDifficulty(DifficultyInstance difficulty) { if (this.rand.nextFloat() < 0.15F * difficulty.getClampedAdditionalDifficulty()) { int i = this.rand.nextInt(2); float f = this.world.getDifficulty() == 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; } boolean flag = true; for (EntityEquipmentSlot entityequipmentslot : EntityEquipmentSlot.values()) { if (entityequipmentslot.getSlotType() == EntityEquipmentSlot.Type.ARMOR) { ItemStack itemstack = this.getItemStackFromSlot(entityequipmentslot); if (!flag && this.rand.nextFloat() < f) { break; } flag = false; if (itemstack.isEmpty()) { Item item = getArmorByChance(entityequipmentslot, i); if (item != null) { this.setItemStackToSlot(entityequipmentslot, new ItemStack(item)); } } } } } } public static EntityEquipmentSlot getSlotForItemStack(ItemStack stack) { final EntityEquipmentSlot slot = stack.getItem().getEquipmentSlot(stack); if (slot != null) return slot; // FORGE: Allow modders to set a non-default equipment slot for a stack; e.g. a non-armor chestplate-slot item if (stack.getItem() != Item.getItemFromBlock(Blocks.PUMPKIN) && stack.getItem() != Items.SKULL) { if (stack.getItem() instanceof ItemArmor) { return ((ItemArmor)stack.getItem()).armorType; } else if (stack.getItem() == Items.ELYTRA) { return EntityEquipmentSlot.CHEST; } else { return stack.getItem().isShield(stack, null) ? EntityEquipmentSlot.OFFHAND : EntityEquipmentSlot.MAINHAND; } } else { return EntityEquipmentSlot.HEAD; } } @Nullable public static Item getArmorByChance(EntityEquipmentSlot slotIn, int chance) { switch (slotIn) { case HEAD: if (chance == 0) { return Items.LEATHER_HELMET; } else if (chance == 1) { return Items.GOLDEN_HELMET; } else if (chance == 2) { return Items.CHAINMAIL_HELMET; } else if (chance == 3) { return Items.IRON_HELMET; } else if (chance == 4) { return Items.DIAMOND_HELMET; } case CHEST: if (chance == 0) { return Items.LEATHER_CHESTPLATE; } else if (chance == 1) { return Items.GOLDEN_CHESTPLATE; } else if (chance == 2) { return Items.CHAINMAIL_CHESTPLATE; } else if (chance == 3) { return Items.IRON_CHESTPLATE; } else if (chance == 4) { return Items.DIAMOND_CHESTPLATE; } case LEGS: if (chance == 0) { return Items.LEATHER_LEGGINGS; } else if (chance == 1) { return Items.GOLDEN_LEGGINGS; } else if (chance == 2) { return Items.CHAINMAIL_LEGGINGS; } else if (chance == 3) { return Items.IRON_LEGGINGS; } else if (chance == 4) { return Items.DIAMOND_LEGGINGS; } case FEET: if (chance == 0) { return Items.LEATHER_BOOTS; } else if (chance == 1) { return Items.GOLDEN_BOOTS; } else if (chance == 2) { return Items.CHAINMAIL_BOOTS; } else if (chance == 3) { return Items.IRON_BOOTS; } else if (chance == 4) { return Items.DIAMOND_BOOTS; } default: return null; } } /** * Enchants Entity's current equipments based on given DifficultyInstance */ protected void setEnchantmentBasedOnDifficulty(DifficultyInstance difficulty) { float f = difficulty.getClampedAdditionalDifficulty(); if (!this.getHeldItemMainhand().isEmpty() && this.rand.nextFloat() < 0.25F * f) { this.setItemStackToSlot(EntityEquipmentSlot.MAINHAND, EnchantmentHelper.addRandomEnchantment(this.rand, this.getHeldItemMainhand(), (int)(5.0F + f * (float)this.rand.nextInt(18)), false)); } for (EntityEquipmentSlot entityequipmentslot : EntityEquipmentSlot.values()) { if (entityequipmentslot.getSlotType() == EntityEquipmentSlot.Type.ARMOR) { ItemStack itemstack = this.getItemStackFromSlot(entityequipmentslot); if (!itemstack.isEmpty() && this.rand.nextFloat() < 0.5F * f) { this.setItemStackToSlot(entityequipmentslot, EnchantmentHelper.addRandomEnchantment(this.rand, itemstack, (int)(5.0F + f * (float)this.rand.nextInt(18)), false)); } } } } /** * Called only once on an entity when first time spawned, via egg, mob spawner, natural spawning etc, but not called * when entity is reloaded from nbt. Mainly used for initializing attributes and inventory */ @Nullable public IEntityLivingData onInitialSpawn(DifficultyInstance difficulty, @Nullable IEntityLivingData livingdata) { this.getEntityAttribute(SharedMonsterAttributes.FOLLOW_RANGE).applyModifier(new AttributeModifier("Random spawn bonus", this.rand.nextGaussian() * 0.05D, 1)); if (this.rand.nextFloat() < 0.05F) { this.setLeftHanded(true); } else { this.setLeftHanded(false); } return livingdata; } /** * 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; } /** * Enable the Entity persistence */ public void enablePersistence() { this.persistenceRequired = true; } public void setDropChance(EntityEquipmentSlot slotIn, float chance) { switch (slotIn.getSlotType()) { case HAND: this.inventoryHandsDropChances[slotIn.getIndex()] = chance; break; case ARMOR: this.inventoryArmorDropChances[slotIn.getIndex()] = chance; } } public boolean canPickUpLoot() { return this.canPickUpLoot; } public void setCanPickUpLoot(boolean canPickup) { this.canPickUpLoot = canPickup; } /** * Return the persistenceRequired field (whether this entity is allowed to naturally despawn) */ public boolean isNoDespawnRequired() { return this.persistenceRequired; } public final boolean processInitialInteract(EntityPlayer player, EnumHand hand) { if (this.getLeashed() && this.getLeashHolder() == player) { this.clearLeashed(true, !player.capabilities.isCreativeMode); return true; } else { ItemStack itemstack = player.getHeldItem(hand); if (itemstack.getItem() == Items.LEAD && this.canBeLeashedTo(player)) { this.setLeashHolder(player, true); itemstack.shrink(1); return true; } else { return this.processInteract(player, hand) ? true : super.processInitialInteract(player, hand); } } } protected boolean processInteract(EntityPlayer player, EnumHand hand) { return false; } /** * Applies logic related to leashes, for example dragging the entity or breaking the leash. */ protected void updateLeashedState() { if (this.leashNBTTag != null) { this.recreateLeash(); } if (this.isLeashed) { if (!this.isEntityAlive()) { this.clearLeashed(true, true); } if (this.leashHolder == null || this.leashHolder.isDead) { this.clearLeashed(true, true); } } } /** * Removes the leash from this entity */ public void clearLeashed(boolean sendPacket, boolean dropLead) { if (this.isLeashed) { this.isLeashed = false; this.leashHolder = null; if (!this.world.isRemote && dropLead) { this.dropItem(Items.LEAD, 1); } if (!this.world.isRemote && sendPacket && this.world instanceof WorldServer) { ((WorldServer)this.world).getEntityTracker().sendToTracking(this, new SPacketEntityAttach(this, (Entity)null)); } } } public boolean canBeLeashedTo(EntityPlayer player) { return !this.getLeashed() && !(this instanceof IMob); } public boolean getLeashed() { return this.isLeashed; } public Entity getLeashHolder() { return this.leashHolder; } /** * Sets the entity to be leashed to. */ public void setLeashHolder(Entity entityIn, boolean sendAttachNotification) { this.isLeashed = true; this.leashHolder = entityIn; if (!this.world.isRemote && sendAttachNotification && this.world instanceof WorldServer) { ((WorldServer)this.world).getEntityTracker().sendToTracking(this, new SPacketEntityAttach(this, this.leashHolder)); } if (this.isRiding()) { this.dismountRidingEntity(); } } public boolean startRiding(Entity entityIn, boolean force) { boolean flag = super.startRiding(entityIn, force); if (flag && this.getLeashed()) { this.clearLeashed(true, true); } return flag; } private void recreateLeash() { if (this.isLeashed && this.leashNBTTag != null) { if (this.leashNBTTag.hasUniqueId("UUID")) { UUID uuid = this.leashNBTTag.getUniqueId("UUID"); for (EntityLivingBase entitylivingbase : this.world.getEntitiesWithinAABB(EntityLivingBase.class, this.getEntityBoundingBox().grow(10.0D))) { if (entitylivingbase.getUniqueID().equals(uuid)) { this.setLeashHolder(entitylivingbase, true); break; } } } else if (this.leashNBTTag.hasKey("X", 99) && this.leashNBTTag.hasKey("Y", 99) && this.leashNBTTag.hasKey("Z", 99)) { BlockPos blockpos = new BlockPos(this.leashNBTTag.getInteger("X"), this.leashNBTTag.getInteger("Y"), this.leashNBTTag.getInteger("Z")); EntityLeashKnot entityleashknot = EntityLeashKnot.getKnotForPosition(this.world, blockpos); if (entityleashknot == null) { entityleashknot = EntityLeashKnot.createKnot(this.world, blockpos); } this.setLeashHolder(entityleashknot, true); } else { this.clearLeashed(false, true); } } this.leashNBTTag = null; } public boolean replaceItemInInventory(int inventorySlot, ItemStack itemStackIn) { EntityEquipmentSlot entityequipmentslot; if (inventorySlot == 98) { entityequipmentslot = EntityEquipmentSlot.MAINHAND; } else if (inventorySlot == 99) { entityequipmentslot = EntityEquipmentSlot.OFFHAND; } else if (inventorySlot == 100 + EntityEquipmentSlot.HEAD.getIndex()) { entityequipmentslot = EntityEquipmentSlot.HEAD; } else if (inventorySlot == 100 + EntityEquipmentSlot.CHEST.getIndex()) { entityequipmentslot = EntityEquipmentSlot.CHEST; } else if (inventorySlot == 100 + EntityEquipmentSlot.LEGS.getIndex()) { entityequipmentslot = EntityEquipmentSlot.LEGS; } else { if (inventorySlot != 100 + EntityEquipmentSlot.FEET.getIndex()) { return false; } entityequipmentslot = EntityEquipmentSlot.FEET; } if (!itemStackIn.isEmpty() && !isItemStackInSlot(entityequipmentslot, itemStackIn) && entityequipmentslot != EntityEquipmentSlot.HEAD) { return false; } else { this.setItemStackToSlot(entityequipmentslot, itemStackIn); return true; } } public boolean canPassengerSteer() { return this.canBeSteered() && super.canPassengerSteer(); } public static boolean isItemStackInSlot(EntityEquipmentSlot slotIn, ItemStack stack) { EntityEquipmentSlot entityequipmentslot = getSlotForItemStack(stack); return entityequipmentslot == slotIn || entityequipmentslot == EntityEquipmentSlot.MAINHAND && slotIn == EntityEquipmentSlot.OFFHAND || entityequipmentslot == EntityEquipmentSlot.OFFHAND && slotIn == EntityEquipmentSlot.MAINHAND; } /** * Returns whether the entity is in a server world */ public boolean isServerWorld() { return super.isServerWorld() && !this.isAIDisabled(); } /** * Set whether this Entity's AI is disabled */ public void setNoAI(boolean disable) { byte b0 = ((Byte)this.dataManager.get(AI_FLAGS)).byteValue(); this.dataManager.set(AI_FLAGS, Byte.valueOf(disable ? (byte)(b0 | 1) : (byte)(b0 & -2))); } public void setLeftHanded(boolean leftHanded) { byte b0 = ((Byte)this.dataManager.get(AI_FLAGS)).byteValue(); this.dataManager.set(AI_FLAGS, Byte.valueOf(leftHanded ? (byte)(b0 | 2) : (byte)(b0 & -3))); } /** * Get whether this Entity's AI is disabled */ public boolean isAIDisabled() { return (((Byte)this.dataManager.get(AI_FLAGS)).byteValue() & 1) != 0; } public boolean isLeftHanded() { return (((Byte)this.dataManager.get(AI_FLAGS)).byteValue() & 2) != 0; } public EnumHandSide getPrimaryHand() { return this.isLeftHanded() ? EnumHandSide.LEFT : EnumHandSide.RIGHT; } public static enum SpawnPlacementType { ON_GROUND, IN_AIR, IN_WATER; private final java.util.function.BiPredicate spawnPredicate; SpawnPlacementType() { this.spawnPredicate = null; } SpawnPlacementType(java.util.function.BiPredicate spawnPredicate) { this.spawnPredicate = spawnPredicate; } public boolean canSpawnAt(World world, BlockPos pos) { return this.spawnPredicate != null ? this.spawnPredicate.test(world, pos) : net.minecraft.world.WorldEntitySpawner.canCreatureTypeSpawnBody(this, world, pos); } } }