3761 lines
118 KiB
Java
3761 lines
118 KiB
Java
package net.minecraft.entity;
|
|
|
|
import com.google.common.collect.Iterables;
|
|
import com.google.common.collect.Lists;
|
|
import com.google.common.collect.Sets;
|
|
import java.util.Arrays;
|
|
import java.util.Collection;
|
|
import java.util.Collections;
|
|
import java.util.List;
|
|
import java.util.Random;
|
|
import java.util.Set;
|
|
import java.util.UUID;
|
|
import javax.annotation.Nullable;
|
|
import net.minecraft.advancements.CriteriaTriggers;
|
|
import net.minecraft.block.Block;
|
|
import net.minecraft.block.BlockFence;
|
|
import net.minecraft.block.BlockFenceGate;
|
|
import net.minecraft.block.BlockLiquid;
|
|
import net.minecraft.block.BlockWall;
|
|
import net.minecraft.block.SoundType;
|
|
import net.minecraft.block.material.EnumPushReaction;
|
|
import net.minecraft.block.material.Material;
|
|
import net.minecraft.block.state.IBlockState;
|
|
import net.minecraft.block.state.pattern.BlockPattern;
|
|
import net.minecraft.command.CommandResultStats;
|
|
import net.minecraft.command.ICommandSender;
|
|
import net.minecraft.crash.CrashReport;
|
|
import net.minecraft.crash.CrashReportCategory;
|
|
import net.minecraft.crash.ICrashReportDetail;
|
|
import net.minecraft.enchantment.EnchantmentHelper;
|
|
import net.minecraft.enchantment.EnchantmentProtection;
|
|
import net.minecraft.entity.effect.EntityLightningBolt;
|
|
import net.minecraft.entity.item.EntityBoat;
|
|
import net.minecraft.entity.item.EntityItem;
|
|
import net.minecraft.entity.player.EntityPlayer;
|
|
import net.minecraft.entity.player.EntityPlayerMP;
|
|
import net.minecraft.init.Blocks;
|
|
import net.minecraft.init.SoundEvents;
|
|
import net.minecraft.inventory.EntityEquipmentSlot;
|
|
import net.minecraft.item.Item;
|
|
import net.minecraft.item.ItemStack;
|
|
import net.minecraft.nbt.NBTTagCompound;
|
|
import net.minecraft.nbt.NBTTagDouble;
|
|
import net.minecraft.nbt.NBTTagFloat;
|
|
import net.minecraft.nbt.NBTTagList;
|
|
import net.minecraft.nbt.NBTTagString;
|
|
import net.minecraft.network.datasync.DataParameter;
|
|
import net.minecraft.network.datasync.DataSerializers;
|
|
import net.minecraft.network.datasync.EntityDataManager;
|
|
import net.minecraft.scoreboard.ScorePlayerTeam;
|
|
import net.minecraft.scoreboard.Team;
|
|
import net.minecraft.server.MinecraftServer;
|
|
import net.minecraft.util.DamageSource;
|
|
import net.minecraft.util.EnumActionResult;
|
|
import net.minecraft.util.EnumBlockRenderType;
|
|
import net.minecraft.util.EnumFacing;
|
|
import net.minecraft.util.EnumHand;
|
|
import net.minecraft.util.EnumParticleTypes;
|
|
import net.minecraft.util.Mirror;
|
|
import net.minecraft.util.ReportedException;
|
|
import net.minecraft.util.ResourceLocation;
|
|
import net.minecraft.util.Rotation;
|
|
import net.minecraft.util.SoundCategory;
|
|
import net.minecraft.util.SoundEvent;
|
|
import net.minecraft.util.datafix.DataFixer;
|
|
import net.minecraft.util.datafix.FixTypes;
|
|
import net.minecraft.util.datafix.IDataFixer;
|
|
import net.minecraft.util.datafix.IDataWalker;
|
|
import net.minecraft.util.math.AxisAlignedBB;
|
|
import net.minecraft.util.math.BlockPos;
|
|
import net.minecraft.util.math.MathHelper;
|
|
import net.minecraft.util.math.RayTraceResult;
|
|
import net.minecraft.util.math.Vec2f;
|
|
import net.minecraft.util.math.Vec3d;
|
|
import net.minecraft.util.text.ITextComponent;
|
|
import net.minecraft.util.text.TextComponentString;
|
|
import net.minecraft.util.text.event.HoverEvent;
|
|
import net.minecraft.util.text.translation.I18n;
|
|
import net.minecraft.world.Explosion;
|
|
import net.minecraft.world.Teleporter;
|
|
import net.minecraft.world.World;
|
|
import net.minecraft.world.WorldServer;
|
|
import net.minecraftforge.fml.relauncher.Side;
|
|
import net.minecraftforge.fml.relauncher.SideOnly;
|
|
import org.apache.logging.log4j.LogManager;
|
|
import org.apache.logging.log4j.Logger;
|
|
|
|
public abstract class Entity implements ICommandSender, net.minecraftforge.common.capabilities.ICapabilitySerializable<NBTTagCompound>
|
|
{
|
|
private static final Logger LOGGER = LogManager.getLogger();
|
|
private static final List<ItemStack> EMPTY_EQUIPMENT = Collections.<ItemStack>emptyList();
|
|
private static final AxisAlignedBB ZERO_AABB = new AxisAlignedBB(0.0D, 0.0D, 0.0D, 0.0D, 0.0D, 0.0D);
|
|
private static double renderDistanceWeight = 1.0D;
|
|
private static int nextEntityID;
|
|
private int entityId;
|
|
/**
|
|
* Blocks entities from spawning when they do their AABB check to make sure the spot is clear of entities that can
|
|
* prevent spawning.
|
|
*/
|
|
public boolean preventEntitySpawning;
|
|
/** List of entities that are riding this entity */
|
|
private final List<Entity> riddenByEntities;
|
|
protected int rideCooldown;
|
|
private Entity ridingEntity;
|
|
public boolean forceSpawn;
|
|
/** Reference to the World object. */
|
|
public World world;
|
|
public double prevPosX;
|
|
public double prevPosY;
|
|
public double prevPosZ;
|
|
/** X position of this entity, located at the center of its bounding box. */
|
|
public double posX;
|
|
/** Y position of this entity, located at the bottom of its bounding box (its feet) */
|
|
public double posY;
|
|
/** Z position of this entity, located at the center of its bounding box. */
|
|
public double posZ;
|
|
/** Entity motion X */
|
|
public double motionX;
|
|
/** Entity motion Y */
|
|
public double motionY;
|
|
/** Entity motion Z */
|
|
public double motionZ;
|
|
/** Entity rotation Yaw */
|
|
public float rotationYaw;
|
|
/** Entity rotation Pitch */
|
|
public float rotationPitch;
|
|
public float prevRotationYaw;
|
|
public float prevRotationPitch;
|
|
/** Axis aligned bounding box. */
|
|
private AxisAlignedBB boundingBox;
|
|
public boolean onGround;
|
|
/** True if after a move this entity has collided with something on X- or Z-axis */
|
|
public boolean collidedHorizontally;
|
|
/** True if after a move this entity has collided with something on Y-axis */
|
|
public boolean collidedVertically;
|
|
/** True if after a move this entity has collided with something either vertically or horizontally */
|
|
public boolean collided;
|
|
/** If true, an {@link SPacketEntityVelocity} will be sent updating this entity's velocity. */
|
|
public boolean velocityChanged;
|
|
protected boolean isInWeb;
|
|
private boolean isOutsideBorder;
|
|
/** gets set by setEntityDead, so this must be the flag whether an Entity is dead (inactive may be better term) */
|
|
public boolean isDead;
|
|
/** How wide this entity is considered to be */
|
|
public float width;
|
|
/** How high this entity is considered to be */
|
|
public float height;
|
|
/** The previous ticks distance walked multiplied by 0.6 */
|
|
public float prevDistanceWalkedModified;
|
|
/** The distance walked multiplied by 0.6 */
|
|
public float distanceWalkedModified;
|
|
public float distanceWalkedOnStepModified;
|
|
public float fallDistance;
|
|
/** The distance that has to be exceeded in order to triger a new step sound and an onEntityWalking event on a block */
|
|
private int nextStepDistance;
|
|
private float nextFlap;
|
|
/** The entity's X coordinate at the previous tick, used to calculate position during rendering routines */
|
|
public double lastTickPosX;
|
|
/** The entity's Y coordinate at the previous tick, used to calculate position during rendering routines */
|
|
public double lastTickPosY;
|
|
/** The entity's Z coordinate at the previous tick, used to calculate position during rendering routines */
|
|
public double lastTickPosZ;
|
|
/**
|
|
* How high this entity can step up when running into a block to try to get over it (currently make note the entity
|
|
* will always step up this amount and not just the amount needed)
|
|
*/
|
|
public float stepHeight;
|
|
/** Whether this entity won't clip with collision or not (make note it won't disable gravity) */
|
|
public boolean noClip;
|
|
/** Reduces the velocity applied by entity collisions by the specified percent. */
|
|
public float entityCollisionReduction;
|
|
protected Random rand;
|
|
/** How many ticks has this entity had ran since being alive */
|
|
public int ticksExisted;
|
|
private int fire;
|
|
/** Whether this entity is currently inside of water (if it handles water movement that is) */
|
|
protected boolean inWater;
|
|
/** Remaining time an entity will be "immune" to further damage after being hurt. */
|
|
public int hurtResistantTime;
|
|
protected boolean firstUpdate;
|
|
protected boolean isImmuneToFire;
|
|
protected EntityDataManager dataManager;
|
|
protected static final DataParameter<Byte> FLAGS = EntityDataManager.<Byte>createKey(Entity.class, DataSerializers.BYTE);
|
|
private static final DataParameter<Integer> AIR = EntityDataManager.<Integer>createKey(Entity.class, DataSerializers.VARINT);
|
|
private static final DataParameter<String> CUSTOM_NAME = EntityDataManager.<String>createKey(Entity.class, DataSerializers.STRING);
|
|
private static final DataParameter<Boolean> CUSTOM_NAME_VISIBLE = EntityDataManager.<Boolean>createKey(Entity.class, DataSerializers.BOOLEAN);
|
|
private static final DataParameter<Boolean> SILENT = EntityDataManager.<Boolean>createKey(Entity.class, DataSerializers.BOOLEAN);
|
|
private static final DataParameter<Boolean> NO_GRAVITY = EntityDataManager.<Boolean>createKey(Entity.class, DataSerializers.BOOLEAN);
|
|
/** Has this entity been added to the chunk its within */
|
|
public boolean addedToChunk;
|
|
public int chunkCoordX;
|
|
public int chunkCoordY;
|
|
public int chunkCoordZ;
|
|
@SideOnly(Side.CLIENT)
|
|
public long serverPosX;
|
|
@SideOnly(Side.CLIENT)
|
|
public long serverPosY;
|
|
@SideOnly(Side.CLIENT)
|
|
public long serverPosZ;
|
|
/**
|
|
* Render entity even if it is outside the camera frustum. Only true in EntityFish for now. Used in RenderGlobal:
|
|
* render if ignoreFrustumCheck or in frustum.
|
|
*/
|
|
public boolean ignoreFrustumCheck;
|
|
public boolean isAirBorne;
|
|
public int timeUntilPortal;
|
|
/** Whether the entity is inside a Portal */
|
|
protected boolean inPortal;
|
|
protected int portalCounter;
|
|
/** Which dimension the player is in (-1 = the Nether, 0 = normal world) */
|
|
public int dimension;
|
|
/** The position of the last portal the entity was in */
|
|
protected BlockPos lastPortalPos;
|
|
/** A horizontal vector related to the position of the last portal the entity was in */
|
|
protected Vec3d lastPortalVec;
|
|
/** A direction related to the position of the last portal the entity was in */
|
|
protected EnumFacing teleportDirection;
|
|
private boolean invulnerable;
|
|
protected UUID entityUniqueID;
|
|
protected String cachedUniqueIdString;
|
|
/** The command result statistics for this Entity. */
|
|
private final CommandResultStats cmdResultStats;
|
|
protected boolean glowing;
|
|
private final Set<String> tags;
|
|
private boolean isPositionDirty;
|
|
private final double[] pistonDeltas;
|
|
private long pistonDeltasGameTime;
|
|
/**
|
|
* Setting this to true will prevent the world from calling {@link #onUpdate()} for this entity.
|
|
*/
|
|
public boolean updateBlocked;
|
|
|
|
public Entity(World worldIn)
|
|
{
|
|
this.entityId = nextEntityID++;
|
|
this.riddenByEntities = Lists.<Entity>newArrayList();
|
|
this.boundingBox = ZERO_AABB;
|
|
this.width = 0.6F;
|
|
this.height = 1.8F;
|
|
this.nextStepDistance = 1;
|
|
this.nextFlap = 1.0F;
|
|
this.rand = new Random();
|
|
this.fire = -this.getFireImmuneTicks();
|
|
this.firstUpdate = true;
|
|
this.entityUniqueID = MathHelper.getRandomUUID(this.rand);
|
|
this.cachedUniqueIdString = this.entityUniqueID.toString();
|
|
this.cmdResultStats = new CommandResultStats();
|
|
this.tags = Sets.<String>newHashSet();
|
|
this.pistonDeltas = new double[] {0.0D, 0.0D, 0.0D};
|
|
this.world = worldIn;
|
|
this.setPosition(0.0D, 0.0D, 0.0D);
|
|
|
|
if (worldIn != null)
|
|
{
|
|
this.dimension = worldIn.provider.getDimension();
|
|
}
|
|
|
|
this.dataManager = new EntityDataManager(this);
|
|
this.dataManager.register(FLAGS, Byte.valueOf((byte)0));
|
|
this.dataManager.register(AIR, Integer.valueOf(300));
|
|
this.dataManager.register(CUSTOM_NAME_VISIBLE, Boolean.valueOf(false));
|
|
this.dataManager.register(CUSTOM_NAME, "");
|
|
this.dataManager.register(SILENT, Boolean.valueOf(false));
|
|
this.dataManager.register(NO_GRAVITY, Boolean.valueOf(false));
|
|
this.entityInit();
|
|
net.minecraftforge.common.MinecraftForge.EVENT_BUS.post(new net.minecraftforge.event.entity.EntityEvent.EntityConstructing(this));
|
|
capabilities = net.minecraftforge.event.ForgeEventFactory.gatherCapabilities(this);
|
|
}
|
|
|
|
/** Forge: Used to store custom data for each entity. */
|
|
private NBTTagCompound customEntityData;
|
|
public boolean captureDrops = false;
|
|
public java.util.ArrayList<EntityItem> capturedDrops = new java.util.ArrayList<EntityItem>();
|
|
private net.minecraftforge.common.capabilities.CapabilityDispatcher capabilities;
|
|
|
|
public int getEntityId()
|
|
{
|
|
return this.entityId;
|
|
}
|
|
|
|
public void setEntityId(int id)
|
|
{
|
|
this.entityId = id;
|
|
}
|
|
|
|
public Set<String> getTags()
|
|
{
|
|
return this.tags;
|
|
}
|
|
|
|
public boolean addTag(String tag)
|
|
{
|
|
if (this.tags.size() >= 1024)
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
this.tags.add(tag);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
public boolean removeTag(String tag)
|
|
{
|
|
return this.tags.remove(tag);
|
|
}
|
|
|
|
/**
|
|
* Called by the /kill command.
|
|
*/
|
|
public void onKillCommand()
|
|
{
|
|
this.setDead();
|
|
}
|
|
|
|
protected abstract void entityInit();
|
|
|
|
public EntityDataManager getDataManager()
|
|
{
|
|
return this.dataManager;
|
|
}
|
|
|
|
public boolean equals(Object p_equals_1_)
|
|
{
|
|
if (p_equals_1_ instanceof Entity)
|
|
{
|
|
return ((Entity)p_equals_1_).entityId == this.entityId;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public int hashCode()
|
|
{
|
|
return this.entityId;
|
|
}
|
|
|
|
/**
|
|
* Keeps moving the entity up so it isn't colliding with blocks and other requirements for this entity to be spawned
|
|
* (only actually used on players though its also on Entity)
|
|
*/
|
|
@SideOnly(Side.CLIENT)
|
|
protected void preparePlayerToSpawn()
|
|
{
|
|
if (this.world != null)
|
|
{
|
|
while (this.posY > 0.0D && this.posY < 256.0D)
|
|
{
|
|
this.setPosition(this.posX, this.posY, this.posZ);
|
|
|
|
if (this.world.getCollisionBoxes(this, this.getEntityBoundingBox()).isEmpty())
|
|
{
|
|
break;
|
|
}
|
|
|
|
++this.posY;
|
|
}
|
|
|
|
this.motionX = 0.0D;
|
|
this.motionY = 0.0D;
|
|
this.motionZ = 0.0D;
|
|
this.rotationPitch = 0.0F;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Will get destroyed next tick.
|
|
*/
|
|
public void setDead()
|
|
{
|
|
this.isDead = true;
|
|
}
|
|
|
|
/**
|
|
* Sets whether this entity should drop its items when setDead() is called. This applies to container minecarts.
|
|
*/
|
|
public void setDropItemsWhenDead(boolean dropWhenDead)
|
|
{
|
|
}
|
|
|
|
/**
|
|
* Sets the width and height of the entity.
|
|
*/
|
|
protected void setSize(float width, float height)
|
|
{
|
|
if (width != this.width || height != this.height)
|
|
{
|
|
float f = this.width;
|
|
this.width = width;
|
|
this.height = height;
|
|
|
|
if (this.width < f)
|
|
{
|
|
double d0 = (double)width / 2.0D;
|
|
this.setEntityBoundingBox(new AxisAlignedBB(this.posX - d0, this.posY, this.posZ - d0, this.posX + d0, this.posY + (double)this.height, this.posZ + d0));
|
|
return;
|
|
}
|
|
|
|
AxisAlignedBB axisalignedbb = this.getEntityBoundingBox();
|
|
this.setEntityBoundingBox(new AxisAlignedBB(axisalignedbb.minX, axisalignedbb.minY, axisalignedbb.minZ, axisalignedbb.minX + (double)this.width, axisalignedbb.minY + (double)this.height, axisalignedbb.minZ + (double)this.width));
|
|
|
|
if (this.width > f && !this.firstUpdate && !this.world.isRemote)
|
|
{
|
|
this.move(MoverType.SELF, (double)(f - this.width), 0.0D, (double)(f - this.width));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets the rotation of the entity.
|
|
*/
|
|
protected void setRotation(float yaw, float pitch)
|
|
{
|
|
this.rotationYaw = yaw % 360.0F;
|
|
this.rotationPitch = pitch % 360.0F;
|
|
}
|
|
|
|
/**
|
|
* Sets the x,y,z of the entity from the given parameters. Also seems to set up a bounding box.
|
|
*/
|
|
public void setPosition(double x, double y, double z)
|
|
{
|
|
this.posX = x;
|
|
this.posY = y;
|
|
this.posZ = z;
|
|
float f = this.width / 2.0F;
|
|
float f1 = this.height;
|
|
this.setEntityBoundingBox(new AxisAlignedBB(x - (double)f, y, z - (double)f, x + (double)f, y + (double)f1, z + (double)f));
|
|
}
|
|
|
|
/**
|
|
* Adds 15% to the entity's yaw and subtracts 15% from the pitch. Clamps pitch from -90 to 90. Both arguments in
|
|
* degrees.
|
|
*/
|
|
@SideOnly(Side.CLIENT)
|
|
public void turn(float yaw, float pitch)
|
|
{
|
|
float f = this.rotationPitch;
|
|
float f1 = this.rotationYaw;
|
|
this.rotationYaw = (float)((double)this.rotationYaw + (double)yaw * 0.15D);
|
|
this.rotationPitch = (float)((double)this.rotationPitch - (double)pitch * 0.15D);
|
|
this.rotationPitch = MathHelper.clamp(this.rotationPitch, -90.0F, 90.0F);
|
|
this.prevRotationPitch += this.rotationPitch - f;
|
|
this.prevRotationYaw += this.rotationYaw - f1;
|
|
|
|
if (this.ridingEntity != null)
|
|
{
|
|
this.ridingEntity.applyOrientationToEntity(this);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Called to update the entity's position/logic.
|
|
*/
|
|
public void onUpdate()
|
|
{
|
|
if (!this.world.isRemote)
|
|
{
|
|
this.setFlag(6, this.isGlowing());
|
|
}
|
|
|
|
this.onEntityUpdate();
|
|
}
|
|
|
|
/**
|
|
* Gets called every tick from main Entity class
|
|
*/
|
|
public void onEntityUpdate()
|
|
{
|
|
this.world.profiler.startSection("entityBaseTick");
|
|
|
|
if (this.isRiding() && this.getRidingEntity().isDead)
|
|
{
|
|
this.dismountRidingEntity();
|
|
}
|
|
|
|
if (this.rideCooldown > 0)
|
|
{
|
|
--this.rideCooldown;
|
|
}
|
|
|
|
this.prevDistanceWalkedModified = this.distanceWalkedModified;
|
|
this.prevPosX = this.posX;
|
|
this.prevPosY = this.posY;
|
|
this.prevPosZ = this.posZ;
|
|
this.prevRotationPitch = this.rotationPitch;
|
|
this.prevRotationYaw = this.rotationYaw;
|
|
|
|
if (!this.world.isRemote && this.world instanceof WorldServer)
|
|
{
|
|
this.world.profiler.startSection("portal");
|
|
|
|
if (this.inPortal)
|
|
{
|
|
MinecraftServer minecraftserver = this.world.getMinecraftServer();
|
|
|
|
if (minecraftserver.getAllowNether())
|
|
{
|
|
if (!this.isRiding())
|
|
{
|
|
int i = this.getMaxInPortalTime();
|
|
|
|
if (this.portalCounter++ >= i)
|
|
{
|
|
this.portalCounter = i;
|
|
this.timeUntilPortal = this.getPortalCooldown();
|
|
int j;
|
|
|
|
if (this.world.provider.getDimensionType().getId() == -1)
|
|
{
|
|
j = 0;
|
|
}
|
|
else
|
|
{
|
|
j = -1;
|
|
}
|
|
|
|
this.changeDimension(j);
|
|
}
|
|
}
|
|
|
|
this.inPortal = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (this.portalCounter > 0)
|
|
{
|
|
this.portalCounter -= 4;
|
|
}
|
|
|
|
if (this.portalCounter < 0)
|
|
{
|
|
this.portalCounter = 0;
|
|
}
|
|
}
|
|
|
|
this.decrementTimeUntilPortal();
|
|
this.world.profiler.endSection();
|
|
}
|
|
|
|
this.spawnRunningParticles();
|
|
this.handleWaterMovement();
|
|
|
|
if (this.world.isRemote)
|
|
{
|
|
this.extinguish();
|
|
}
|
|
else if (this.fire > 0)
|
|
{
|
|
if (this.isImmuneToFire)
|
|
{
|
|
this.fire -= 4;
|
|
|
|
if (this.fire < 0)
|
|
{
|
|
this.extinguish();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (this.fire % 20 == 0)
|
|
{
|
|
this.attackEntityFrom(DamageSource.ON_FIRE, 1.0F);
|
|
}
|
|
|
|
--this.fire;
|
|
}
|
|
}
|
|
|
|
if (this.isInLava())
|
|
{
|
|
this.setOnFireFromLava();
|
|
this.fallDistance *= 0.5F;
|
|
}
|
|
|
|
if (this.posY < -64.0D)
|
|
{
|
|
this.outOfWorld();
|
|
}
|
|
|
|
if (!this.world.isRemote)
|
|
{
|
|
this.setFlag(0, this.fire > 0);
|
|
}
|
|
|
|
this.firstUpdate = false;
|
|
this.world.profiler.endSection();
|
|
}
|
|
|
|
/**
|
|
* Decrements the counter for the remaining time until the entity may use a portal again.
|
|
*/
|
|
protected void decrementTimeUntilPortal()
|
|
{
|
|
if (this.timeUntilPortal > 0)
|
|
{
|
|
--this.timeUntilPortal;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return the amount of time this entity should stay in a portal before being transported.
|
|
*/
|
|
public int getMaxInPortalTime()
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* Called whenever the entity is walking inside of lava.
|
|
*/
|
|
protected void setOnFireFromLava()
|
|
{
|
|
if (!this.isImmuneToFire)
|
|
{
|
|
this.attackEntityFrom(DamageSource.LAVA, 4.0F);
|
|
this.setFire(15);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets entity to burn for x amount of seconds, cannot lower amount of existing fire.
|
|
*/
|
|
public void setFire(int seconds)
|
|
{
|
|
int i = seconds * 20;
|
|
|
|
if (this instanceof EntityLivingBase)
|
|
{
|
|
i = EnchantmentProtection.getFireTimeForEntity((EntityLivingBase)this, i);
|
|
}
|
|
|
|
if (this.fire < i)
|
|
{
|
|
this.fire = i;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Removes fire from entity.
|
|
*/
|
|
public void extinguish()
|
|
{
|
|
this.fire = 0;
|
|
}
|
|
|
|
/**
|
|
* sets the dead flag. Used when you fall off the bottom of the world.
|
|
*/
|
|
protected void outOfWorld()
|
|
{
|
|
this.setDead();
|
|
}
|
|
|
|
/**
|
|
* Checks if the offset position from the entity's current position is inside of a liquid.
|
|
*/
|
|
public boolean isOffsetPositionInLiquid(double x, double y, double z)
|
|
{
|
|
AxisAlignedBB axisalignedbb = this.getEntityBoundingBox().offset(x, y, z);
|
|
return this.isLiquidPresentInAABB(axisalignedbb);
|
|
}
|
|
|
|
/**
|
|
* Determines if a liquid is present within the specified AxisAlignedBB.
|
|
*/
|
|
private boolean isLiquidPresentInAABB(AxisAlignedBB bb)
|
|
{
|
|
return this.world.getCollisionBoxes(this, bb).isEmpty() && !this.world.containsAnyLiquid(bb);
|
|
}
|
|
|
|
/**
|
|
* Tries to move the entity towards the specified location.
|
|
*/
|
|
public void move(MoverType type, double x, double y, double z)
|
|
{
|
|
if (this.noClip)
|
|
{
|
|
this.setEntityBoundingBox(this.getEntityBoundingBox().offset(x, y, z));
|
|
this.resetPositionToBB();
|
|
}
|
|
else
|
|
{
|
|
if (type == MoverType.PISTON)
|
|
{
|
|
long i = this.world.getTotalWorldTime();
|
|
|
|
if (i != this.pistonDeltasGameTime)
|
|
{
|
|
Arrays.fill(this.pistonDeltas, 0.0D);
|
|
this.pistonDeltasGameTime = i;
|
|
}
|
|
|
|
if (x != 0.0D)
|
|
{
|
|
int j = EnumFacing.Axis.X.ordinal();
|
|
double d0 = MathHelper.clamp(x + this.pistonDeltas[j], -0.51D, 0.51D);
|
|
x = d0 - this.pistonDeltas[j];
|
|
this.pistonDeltas[j] = d0;
|
|
|
|
if (Math.abs(x) <= 9.999999747378752E-6D)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
else if (y != 0.0D)
|
|
{
|
|
int l4 = EnumFacing.Axis.Y.ordinal();
|
|
double d12 = MathHelper.clamp(y + this.pistonDeltas[l4], -0.51D, 0.51D);
|
|
y = d12 - this.pistonDeltas[l4];
|
|
this.pistonDeltas[l4] = d12;
|
|
|
|
if (Math.abs(y) <= 9.999999747378752E-6D)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (z == 0.0D)
|
|
{
|
|
return;
|
|
}
|
|
|
|
int i5 = EnumFacing.Axis.Z.ordinal();
|
|
double d13 = MathHelper.clamp(z + this.pistonDeltas[i5], -0.51D, 0.51D);
|
|
z = d13 - this.pistonDeltas[i5];
|
|
this.pistonDeltas[i5] = d13;
|
|
|
|
if (Math.abs(z) <= 9.999999747378752E-6D)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
this.world.profiler.startSection("move");
|
|
double d10 = this.posX;
|
|
double d11 = this.posY;
|
|
double d1 = this.posZ;
|
|
|
|
if (this.isInWeb)
|
|
{
|
|
this.isInWeb = false;
|
|
x *= 0.25D;
|
|
y *= 0.05000000074505806D;
|
|
z *= 0.25D;
|
|
this.motionX = 0.0D;
|
|
this.motionY = 0.0D;
|
|
this.motionZ = 0.0D;
|
|
}
|
|
|
|
double d2 = x;
|
|
double d3 = y;
|
|
double d4 = z;
|
|
|
|
if ((type == MoverType.SELF || type == MoverType.PLAYER) && this.onGround && this.isSneaking() && this instanceof EntityPlayer)
|
|
{
|
|
for (double d5 = 0.05D; x != 0.0D && this.world.getCollisionBoxes(this, this.getEntityBoundingBox().offset(x, (double)(-this.stepHeight), 0.0D)).isEmpty(); d2 = x)
|
|
{
|
|
if (x < 0.05D && x >= -0.05D)
|
|
{
|
|
x = 0.0D;
|
|
}
|
|
else if (x > 0.0D)
|
|
{
|
|
x -= 0.05D;
|
|
}
|
|
else
|
|
{
|
|
x += 0.05D;
|
|
}
|
|
}
|
|
|
|
for (; z != 0.0D && this.world.getCollisionBoxes(this, this.getEntityBoundingBox().offset(0.0D, (double)(-this.stepHeight), z)).isEmpty(); d4 = z)
|
|
{
|
|
if (z < 0.05D && z >= -0.05D)
|
|
{
|
|
z = 0.0D;
|
|
}
|
|
else if (z > 0.0D)
|
|
{
|
|
z -= 0.05D;
|
|
}
|
|
else
|
|
{
|
|
z += 0.05D;
|
|
}
|
|
}
|
|
|
|
for (; x != 0.0D && z != 0.0D && this.world.getCollisionBoxes(this, this.getEntityBoundingBox().offset(x, (double)(-this.stepHeight), z)).isEmpty(); d4 = z)
|
|
{
|
|
if (x < 0.05D && x >= -0.05D)
|
|
{
|
|
x = 0.0D;
|
|
}
|
|
else if (x > 0.0D)
|
|
{
|
|
x -= 0.05D;
|
|
}
|
|
else
|
|
{
|
|
x += 0.05D;
|
|
}
|
|
|
|
d2 = x;
|
|
|
|
if (z < 0.05D && z >= -0.05D)
|
|
{
|
|
z = 0.0D;
|
|
}
|
|
else if (z > 0.0D)
|
|
{
|
|
z -= 0.05D;
|
|
}
|
|
else
|
|
{
|
|
z += 0.05D;
|
|
}
|
|
}
|
|
}
|
|
|
|
List<AxisAlignedBB> list1 = this.world.getCollisionBoxes(this, this.getEntityBoundingBox().expand(x, y, z));
|
|
AxisAlignedBB axisalignedbb = this.getEntityBoundingBox();
|
|
|
|
if (y != 0.0D)
|
|
{
|
|
int k = 0;
|
|
|
|
for (int l = list1.size(); k < l; ++k)
|
|
{
|
|
y = ((AxisAlignedBB)list1.get(k)).calculateYOffset(this.getEntityBoundingBox(), y);
|
|
}
|
|
|
|
this.setEntityBoundingBox(this.getEntityBoundingBox().offset(0.0D, y, 0.0D));
|
|
}
|
|
|
|
if (x != 0.0D)
|
|
{
|
|
int j5 = 0;
|
|
|
|
for (int l5 = list1.size(); j5 < l5; ++j5)
|
|
{
|
|
x = ((AxisAlignedBB)list1.get(j5)).calculateXOffset(this.getEntityBoundingBox(), x);
|
|
}
|
|
|
|
if (x != 0.0D)
|
|
{
|
|
this.setEntityBoundingBox(this.getEntityBoundingBox().offset(x, 0.0D, 0.0D));
|
|
}
|
|
}
|
|
|
|
if (z != 0.0D)
|
|
{
|
|
int k5 = 0;
|
|
|
|
for (int i6 = list1.size(); k5 < i6; ++k5)
|
|
{
|
|
z = ((AxisAlignedBB)list1.get(k5)).calculateZOffset(this.getEntityBoundingBox(), z);
|
|
}
|
|
|
|
if (z != 0.0D)
|
|
{
|
|
this.setEntityBoundingBox(this.getEntityBoundingBox().offset(0.0D, 0.0D, z));
|
|
}
|
|
}
|
|
|
|
boolean flag = this.onGround || d3 != y && d3 < 0.0D;
|
|
|
|
if (this.stepHeight > 0.0F && flag && (d2 != x || d4 != z))
|
|
{
|
|
double d14 = x;
|
|
double d6 = y;
|
|
double d7 = z;
|
|
AxisAlignedBB axisalignedbb1 = this.getEntityBoundingBox();
|
|
this.setEntityBoundingBox(axisalignedbb);
|
|
y = (double)this.stepHeight;
|
|
List<AxisAlignedBB> list = this.world.getCollisionBoxes(this, this.getEntityBoundingBox().expand(d2, y, d4));
|
|
AxisAlignedBB axisalignedbb2 = this.getEntityBoundingBox();
|
|
AxisAlignedBB axisalignedbb3 = axisalignedbb2.expand(d2, 0.0D, d4);
|
|
double d8 = y;
|
|
int j1 = 0;
|
|
|
|
for (int k1 = list.size(); j1 < k1; ++j1)
|
|
{
|
|
d8 = ((AxisAlignedBB)list.get(j1)).calculateYOffset(axisalignedbb3, d8);
|
|
}
|
|
|
|
axisalignedbb2 = axisalignedbb2.offset(0.0D, d8, 0.0D);
|
|
double d18 = d2;
|
|
int l1 = 0;
|
|
|
|
for (int i2 = list.size(); l1 < i2; ++l1)
|
|
{
|
|
d18 = ((AxisAlignedBB)list.get(l1)).calculateXOffset(axisalignedbb2, d18);
|
|
}
|
|
|
|
axisalignedbb2 = axisalignedbb2.offset(d18, 0.0D, 0.0D);
|
|
double d19 = d4;
|
|
int j2 = 0;
|
|
|
|
for (int k2 = list.size(); j2 < k2; ++j2)
|
|
{
|
|
d19 = ((AxisAlignedBB)list.get(j2)).calculateZOffset(axisalignedbb2, d19);
|
|
}
|
|
|
|
axisalignedbb2 = axisalignedbb2.offset(0.0D, 0.0D, d19);
|
|
AxisAlignedBB axisalignedbb4 = this.getEntityBoundingBox();
|
|
double d20 = y;
|
|
int l2 = 0;
|
|
|
|
for (int i3 = list.size(); l2 < i3; ++l2)
|
|
{
|
|
d20 = ((AxisAlignedBB)list.get(l2)).calculateYOffset(axisalignedbb4, d20);
|
|
}
|
|
|
|
axisalignedbb4 = axisalignedbb4.offset(0.0D, d20, 0.0D);
|
|
double d21 = d2;
|
|
int j3 = 0;
|
|
|
|
for (int k3 = list.size(); j3 < k3; ++j3)
|
|
{
|
|
d21 = ((AxisAlignedBB)list.get(j3)).calculateXOffset(axisalignedbb4, d21);
|
|
}
|
|
|
|
axisalignedbb4 = axisalignedbb4.offset(d21, 0.0D, 0.0D);
|
|
double d22 = d4;
|
|
int l3 = 0;
|
|
|
|
for (int i4 = list.size(); l3 < i4; ++l3)
|
|
{
|
|
d22 = ((AxisAlignedBB)list.get(l3)).calculateZOffset(axisalignedbb4, d22);
|
|
}
|
|
|
|
axisalignedbb4 = axisalignedbb4.offset(0.0D, 0.0D, d22);
|
|
double d23 = d18 * d18 + d19 * d19;
|
|
double d9 = d21 * d21 + d22 * d22;
|
|
|
|
if (d23 > d9)
|
|
{
|
|
x = d18;
|
|
z = d19;
|
|
y = -d8;
|
|
this.setEntityBoundingBox(axisalignedbb2);
|
|
}
|
|
else
|
|
{
|
|
x = d21;
|
|
z = d22;
|
|
y = -d20;
|
|
this.setEntityBoundingBox(axisalignedbb4);
|
|
}
|
|
|
|
int j4 = 0;
|
|
|
|
for (int k4 = list.size(); j4 < k4; ++j4)
|
|
{
|
|
y = ((AxisAlignedBB)list.get(j4)).calculateYOffset(this.getEntityBoundingBox(), y);
|
|
}
|
|
|
|
this.setEntityBoundingBox(this.getEntityBoundingBox().offset(0.0D, y, 0.0D));
|
|
|
|
if (d14 * d14 + d7 * d7 >= x * x + z * z)
|
|
{
|
|
x = d14;
|
|
y = d6;
|
|
z = d7;
|
|
this.setEntityBoundingBox(axisalignedbb1);
|
|
}
|
|
}
|
|
|
|
this.world.profiler.endSection();
|
|
this.world.profiler.startSection("rest");
|
|
this.resetPositionToBB();
|
|
this.collidedHorizontally = d2 != x || d4 != z;
|
|
this.collidedVertically = d3 != y;
|
|
this.onGround = this.collidedVertically && d3 < 0.0D;
|
|
this.collided = this.collidedHorizontally || this.collidedVertically;
|
|
int j6 = MathHelper.floor(this.posX);
|
|
int i1 = MathHelper.floor(this.posY - 0.20000000298023224D);
|
|
int k6 = MathHelper.floor(this.posZ);
|
|
BlockPos blockpos = new BlockPos(j6, i1, k6);
|
|
IBlockState iblockstate = this.world.getBlockState(blockpos);
|
|
|
|
if (iblockstate.getMaterial() == Material.AIR)
|
|
{
|
|
BlockPos blockpos1 = blockpos.down();
|
|
IBlockState iblockstate1 = this.world.getBlockState(blockpos1);
|
|
Block block1 = iblockstate1.getBlock();
|
|
|
|
if (block1 instanceof BlockFence || block1 instanceof BlockWall || block1 instanceof BlockFenceGate)
|
|
{
|
|
iblockstate = iblockstate1;
|
|
blockpos = blockpos1;
|
|
}
|
|
}
|
|
|
|
this.updateFallState(y, this.onGround, iblockstate, blockpos);
|
|
|
|
if (d2 != x)
|
|
{
|
|
this.motionX = 0.0D;
|
|
}
|
|
|
|
if (d4 != z)
|
|
{
|
|
this.motionZ = 0.0D;
|
|
}
|
|
|
|
Block block = iblockstate.getBlock();
|
|
|
|
if (d3 != y)
|
|
{
|
|
block.onLanded(this.world, this);
|
|
}
|
|
|
|
if (this.canTriggerWalking() && (!this.onGround || !this.isSneaking() || !(this instanceof EntityPlayer)) && !this.isRiding())
|
|
{
|
|
double d15 = this.posX - d10;
|
|
double d16 = this.posY - d11;
|
|
double d17 = this.posZ - d1;
|
|
|
|
if (block != Blocks.LADDER)
|
|
{
|
|
d16 = 0.0D;
|
|
}
|
|
|
|
if (block != null && this.onGround)
|
|
{
|
|
block.onEntityWalk(this.world, blockpos, this);
|
|
}
|
|
|
|
this.distanceWalkedModified = (float)((double)this.distanceWalkedModified + (double)MathHelper.sqrt(d15 * d15 + d17 * d17) * 0.6D);
|
|
this.distanceWalkedOnStepModified = (float)((double)this.distanceWalkedOnStepModified + (double)MathHelper.sqrt(d15 * d15 + d16 * d16 + d17 * d17) * 0.6D);
|
|
|
|
if (this.distanceWalkedOnStepModified > (float)this.nextStepDistance && iblockstate.getMaterial() != Material.AIR)
|
|
{
|
|
this.nextStepDistance = (int)this.distanceWalkedOnStepModified + 1;
|
|
|
|
if (this.isInWater())
|
|
{
|
|
Entity entity = this.isBeingRidden() && this.getControllingPassenger() != null ? this.getControllingPassenger() : this;
|
|
float f = entity == this ? 0.35F : 0.4F;
|
|
float f1 = MathHelper.sqrt(entity.motionX * entity.motionX * 0.20000000298023224D + entity.motionY * entity.motionY + entity.motionZ * entity.motionZ * 0.20000000298023224D) * f;
|
|
|
|
if (f1 > 1.0F)
|
|
{
|
|
f1 = 1.0F;
|
|
}
|
|
|
|
this.playSound(this.getSwimSound(), f1, 1.0F + (this.rand.nextFloat() - this.rand.nextFloat()) * 0.4F);
|
|
}
|
|
else
|
|
{
|
|
this.playStepSound(blockpos, block);
|
|
}
|
|
}
|
|
else if (this.distanceWalkedOnStepModified > this.nextFlap && this.makeFlySound() && iblockstate.getMaterial() == Material.AIR)
|
|
{
|
|
this.nextFlap = this.playFlySound(this.distanceWalkedOnStepModified);
|
|
}
|
|
}
|
|
|
|
try
|
|
{
|
|
this.doBlockCollisions();
|
|
}
|
|
catch (Throwable throwable)
|
|
{
|
|
CrashReport crashreport = CrashReport.makeCrashReport(throwable, "Checking entity block collision");
|
|
CrashReportCategory crashreportcategory = crashreport.makeCategory("Entity being checked for collision");
|
|
this.addEntityCrashInfo(crashreportcategory);
|
|
throw new ReportedException(crashreport);
|
|
}
|
|
|
|
boolean flag1 = this.isWet();
|
|
|
|
if (this.world.isFlammableWithin(this.getEntityBoundingBox().shrink(0.001D)))
|
|
{
|
|
this.dealFireDamage(1);
|
|
|
|
if (!flag1)
|
|
{
|
|
++this.fire;
|
|
|
|
if (this.fire == 0)
|
|
{
|
|
this.setFire(8);
|
|
}
|
|
}
|
|
}
|
|
else if (this.fire <= 0)
|
|
{
|
|
this.fire = -this.getFireImmuneTicks();
|
|
}
|
|
|
|
if (flag1 && this.isBurning())
|
|
{
|
|
this.playSound(SoundEvents.ENTITY_GENERIC_EXTINGUISH_FIRE, 0.7F, 1.6F + (this.rand.nextFloat() - this.rand.nextFloat()) * 0.4F);
|
|
this.fire = -this.getFireImmuneTicks();
|
|
}
|
|
|
|
this.world.profiler.endSection();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Resets the entity's position to the center (planar) and bottom (vertical) points of its bounding box.
|
|
*/
|
|
public void resetPositionToBB()
|
|
{
|
|
AxisAlignedBB axisalignedbb = this.getEntityBoundingBox();
|
|
this.posX = (axisalignedbb.minX + axisalignedbb.maxX) / 2.0D;
|
|
this.posY = axisalignedbb.minY;
|
|
this.posZ = (axisalignedbb.minZ + axisalignedbb.maxZ) / 2.0D;
|
|
}
|
|
|
|
protected SoundEvent getSwimSound()
|
|
{
|
|
return SoundEvents.ENTITY_GENERIC_SWIM;
|
|
}
|
|
|
|
protected SoundEvent getSplashSound()
|
|
{
|
|
return SoundEvents.ENTITY_GENERIC_SPLASH;
|
|
}
|
|
|
|
protected void doBlockCollisions()
|
|
{
|
|
AxisAlignedBB axisalignedbb = this.getEntityBoundingBox();
|
|
BlockPos.PooledMutableBlockPos blockpos$pooledmutableblockpos = BlockPos.PooledMutableBlockPos.retain(axisalignedbb.minX + 0.001D, axisalignedbb.minY + 0.001D, axisalignedbb.minZ + 0.001D);
|
|
BlockPos.PooledMutableBlockPos blockpos$pooledmutableblockpos1 = BlockPos.PooledMutableBlockPos.retain(axisalignedbb.maxX - 0.001D, axisalignedbb.maxY - 0.001D, axisalignedbb.maxZ - 0.001D);
|
|
BlockPos.PooledMutableBlockPos blockpos$pooledmutableblockpos2 = BlockPos.PooledMutableBlockPos.retain();
|
|
|
|
if (this.world.isAreaLoaded(blockpos$pooledmutableblockpos, blockpos$pooledmutableblockpos1))
|
|
{
|
|
for (int i = blockpos$pooledmutableblockpos.getX(); i <= blockpos$pooledmutableblockpos1.getX(); ++i)
|
|
{
|
|
for (int j = blockpos$pooledmutableblockpos.getY(); j <= blockpos$pooledmutableblockpos1.getY(); ++j)
|
|
{
|
|
for (int k = blockpos$pooledmutableblockpos.getZ(); k <= blockpos$pooledmutableblockpos1.getZ(); ++k)
|
|
{
|
|
blockpos$pooledmutableblockpos2.setPos(i, j, k);
|
|
IBlockState iblockstate = this.world.getBlockState(blockpos$pooledmutableblockpos2);
|
|
|
|
try
|
|
{
|
|
iblockstate.getBlock().onEntityCollidedWithBlock(this.world, blockpos$pooledmutableblockpos2, iblockstate, this);
|
|
this.onInsideBlock(iblockstate);
|
|
}
|
|
catch (Throwable throwable)
|
|
{
|
|
CrashReport crashreport = CrashReport.makeCrashReport(throwable, "Colliding entity with block");
|
|
CrashReportCategory crashreportcategory = crashreport.makeCategory("Block being collided with");
|
|
CrashReportCategory.addBlockInfo(crashreportcategory, blockpos$pooledmutableblockpos2, iblockstate);
|
|
throw new ReportedException(crashreport);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
blockpos$pooledmutableblockpos.release();
|
|
blockpos$pooledmutableblockpos1.release();
|
|
blockpos$pooledmutableblockpos2.release();
|
|
}
|
|
|
|
protected void onInsideBlock(IBlockState p_191955_1_)
|
|
{
|
|
}
|
|
|
|
protected void playStepSound(BlockPos pos, Block blockIn)
|
|
{
|
|
SoundType soundtype = blockIn.getSoundType(world.getBlockState(pos), world, pos, this);
|
|
|
|
if (this.world.getBlockState(pos.up()).getBlock() == Blocks.SNOW_LAYER)
|
|
{
|
|
soundtype = Blocks.SNOW_LAYER.getSoundType();
|
|
this.playSound(soundtype.getStepSound(), soundtype.getVolume() * 0.15F, soundtype.getPitch());
|
|
}
|
|
else if (!blockIn.getDefaultState().getMaterial().isLiquid())
|
|
{
|
|
this.playSound(soundtype.getStepSound(), soundtype.getVolume() * 0.15F, soundtype.getPitch());
|
|
}
|
|
}
|
|
|
|
protected float playFlySound(float p_191954_1_)
|
|
{
|
|
return 0.0F;
|
|
}
|
|
|
|
protected boolean makeFlySound()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
public void playSound(SoundEvent soundIn, float volume, float pitch)
|
|
{
|
|
if (!this.isSilent())
|
|
{
|
|
this.world.playSound((EntityPlayer)null, this.posX, this.posY, this.posZ, soundIn, this.getSoundCategory(), volume, pitch);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @return True if this entity will not play sounds
|
|
*/
|
|
public boolean isSilent()
|
|
{
|
|
return ((Boolean)this.dataManager.get(SILENT)).booleanValue();
|
|
}
|
|
|
|
/**
|
|
* When set to true the entity will not play sounds.
|
|
*/
|
|
public void setSilent(boolean isSilent)
|
|
{
|
|
this.dataManager.set(SILENT, Boolean.valueOf(isSilent));
|
|
}
|
|
|
|
public boolean hasNoGravity()
|
|
{
|
|
return ((Boolean)this.dataManager.get(NO_GRAVITY)).booleanValue();
|
|
}
|
|
|
|
public void setNoGravity(boolean noGravity)
|
|
{
|
|
this.dataManager.set(NO_GRAVITY, Boolean.valueOf(noGravity));
|
|
}
|
|
|
|
/**
|
|
* returns if this entity triggers Block.onEntityWalking on the blocks they walk on. used for spiders and wolves to
|
|
* prevent them from trampling crops
|
|
*/
|
|
protected boolean canTriggerWalking()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
protected void updateFallState(double y, boolean onGroundIn, IBlockState state, BlockPos pos)
|
|
{
|
|
if (onGroundIn)
|
|
{
|
|
if (this.fallDistance > 0.0F)
|
|
{
|
|
state.getBlock().onFallenUpon(this.world, pos, this, this.fallDistance);
|
|
}
|
|
|
|
this.fallDistance = 0.0F;
|
|
}
|
|
else if (y < 0.0D)
|
|
{
|
|
this.fallDistance = (float)((double)this.fallDistance - y);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the <b>solid</b> collision bounding box for this entity. Used to make (e.g.) boats solid. Return null if
|
|
* this entity is not solid.
|
|
*
|
|
* For general purposes, use {@link #width} and {@link #height}.
|
|
*
|
|
* @see getEntityBoundingBox
|
|
*/
|
|
@Nullable
|
|
public AxisAlignedBB getCollisionBoundingBox()
|
|
{
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Will deal the specified amount of fire damage to the entity if the entity isn't immune to fire damage.
|
|
*/
|
|
protected void dealFireDamage(int amount)
|
|
{
|
|
if (!this.isImmuneToFire)
|
|
{
|
|
this.attackEntityFrom(DamageSource.IN_FIRE, (float)amount);
|
|
}
|
|
}
|
|
|
|
public final boolean isImmuneToFire()
|
|
{
|
|
return this.isImmuneToFire;
|
|
}
|
|
|
|
public void fall(float distance, float damageMultiplier)
|
|
{
|
|
if (this.isBeingRidden())
|
|
{
|
|
for (Entity entity : this.getPassengers())
|
|
{
|
|
entity.fall(distance, damageMultiplier);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks if this entity is either in water or on an open air block in rain (used in wolves).
|
|
*/
|
|
public boolean isWet()
|
|
{
|
|
if (this.inWater)
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
BlockPos.PooledMutableBlockPos blockpos$pooledmutableblockpos = BlockPos.PooledMutableBlockPos.retain(this.posX, this.posY, this.posZ);
|
|
|
|
if (!this.world.isRainingAt(blockpos$pooledmutableblockpos) && !this.world.isRainingAt(blockpos$pooledmutableblockpos.setPos(this.posX, this.posY + (double)this.height, this.posZ)))
|
|
{
|
|
blockpos$pooledmutableblockpos.release();
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
blockpos$pooledmutableblockpos.release();
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks if this entity is inside water (if inWater field is true as a result of handleWaterMovement() returning
|
|
* true)
|
|
*/
|
|
public boolean isInWater()
|
|
{
|
|
return this.inWater;
|
|
}
|
|
|
|
public boolean isOverWater()
|
|
{
|
|
return this.world.handleMaterialAcceleration(this.getEntityBoundingBox().grow(0.0D, -20.0D, 0.0D).shrink(0.001D), Material.WATER, this);
|
|
}
|
|
|
|
/**
|
|
* Returns if this entity is in water and will end up adding the waters velocity to the entity
|
|
*/
|
|
public boolean handleWaterMovement()
|
|
{
|
|
if (this.getRidingEntity() instanceof EntityBoat)
|
|
{
|
|
this.inWater = false;
|
|
}
|
|
else if (this.world.handleMaterialAcceleration(this.getEntityBoundingBox().grow(0.0D, -0.4000000059604645D, 0.0D).shrink(0.001D), Material.WATER, this))
|
|
{
|
|
if (!this.inWater && !this.firstUpdate)
|
|
{
|
|
this.doWaterSplashEffect();
|
|
}
|
|
|
|
this.fallDistance = 0.0F;
|
|
this.inWater = true;
|
|
this.extinguish();
|
|
}
|
|
else
|
|
{
|
|
this.inWater = false;
|
|
}
|
|
|
|
return this.inWater;
|
|
}
|
|
|
|
/**
|
|
* Plays the {@link #getSplashSound() splash sound}, and the {@link ParticleType#WATER_BUBBLE} and {@link
|
|
* ParticleType#WATER_SPLASH} particles.
|
|
*/
|
|
protected void doWaterSplashEffect()
|
|
{
|
|
Entity entity = this.isBeingRidden() && this.getControllingPassenger() != null ? this.getControllingPassenger() : this;
|
|
float f = entity == this ? 0.2F : 0.9F;
|
|
float f1 = MathHelper.sqrt(entity.motionX * entity.motionX * 0.20000000298023224D + entity.motionY * entity.motionY + entity.motionZ * entity.motionZ * 0.20000000298023224D) * f;
|
|
|
|
if (f1 > 1.0F)
|
|
{
|
|
f1 = 1.0F;
|
|
}
|
|
|
|
this.playSound(this.getSplashSound(), f1, 1.0F + (this.rand.nextFloat() - this.rand.nextFloat()) * 0.4F);
|
|
float f2 = (float)MathHelper.floor(this.getEntityBoundingBox().minY);
|
|
|
|
for (int i = 0; (float)i < 1.0F + this.width * 20.0F; ++i)
|
|
{
|
|
float f3 = (this.rand.nextFloat() * 2.0F - 1.0F) * this.width;
|
|
float f4 = (this.rand.nextFloat() * 2.0F - 1.0F) * this.width;
|
|
this.world.spawnParticle(EnumParticleTypes.WATER_BUBBLE, this.posX + (double)f3, (double)(f2 + 1.0F), this.posZ + (double)f4, this.motionX, this.motionY - (double)(this.rand.nextFloat() * 0.2F), this.motionZ);
|
|
}
|
|
|
|
for (int j = 0; (float)j < 1.0F + this.width * 20.0F; ++j)
|
|
{
|
|
float f5 = (this.rand.nextFloat() * 2.0F - 1.0F) * this.width;
|
|
float f6 = (this.rand.nextFloat() * 2.0F - 1.0F) * this.width;
|
|
this.world.spawnParticle(EnumParticleTypes.WATER_SPLASH, this.posX + (double)f5, (double)(f2 + 1.0F), this.posZ + (double)f6, this.motionX, this.motionY, this.motionZ);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Attempts to create sprinting particles if the entity is sprinting and not in water.
|
|
*/
|
|
public void spawnRunningParticles()
|
|
{
|
|
if (this.isSprinting() && !this.isInWater())
|
|
{
|
|
this.createRunningParticles();
|
|
}
|
|
}
|
|
|
|
protected void createRunningParticles()
|
|
{
|
|
int i = MathHelper.floor(this.posX);
|
|
int j = MathHelper.floor(this.posY - 0.20000000298023224D);
|
|
int k = MathHelper.floor(this.posZ);
|
|
BlockPos blockpos = new BlockPos(i, j, k);
|
|
IBlockState iblockstate = this.world.getBlockState(blockpos);
|
|
|
|
if(!iblockstate.getBlock().addRunningEffects(iblockstate, world, blockpos, this))
|
|
if (iblockstate.getRenderType() != EnumBlockRenderType.INVISIBLE)
|
|
{
|
|
this.world.spawnParticle(EnumParticleTypes.BLOCK_CRACK, this.posX + ((double)this.rand.nextFloat() - 0.5D) * (double)this.width, this.getEntityBoundingBox().minY + 0.1D, this.posZ + ((double)this.rand.nextFloat() - 0.5D) * (double)this.width, -this.motionX * 4.0D, 1.5D, -this.motionZ * 4.0D, Block.getStateId(iblockstate));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks if the current block the entity is within of the specified material type
|
|
*/
|
|
public boolean isInsideOfMaterial(Material materialIn)
|
|
{
|
|
if (this.getRidingEntity() instanceof EntityBoat)
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
double d0 = this.posY + (double)this.getEyeHeight();
|
|
BlockPos blockpos = new BlockPos(this.posX, d0, this.posZ);
|
|
IBlockState iblockstate = this.world.getBlockState(blockpos);
|
|
|
|
Boolean result = iblockstate.getBlock().isEntityInsideMaterial(this.world, blockpos, iblockstate, this, d0, materialIn, true);
|
|
if (result != null) return result;
|
|
|
|
if (iblockstate.getMaterial() == materialIn)
|
|
{
|
|
return net.minecraftforge.common.ForgeHooks.isInsideOfMaterial(materialIn, this, blockpos);
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
public boolean isInLava()
|
|
{
|
|
return this.world.isMaterialInBB(this.getEntityBoundingBox().grow(-0.10000000149011612D, -0.4000000059604645D, -0.10000000149011612D), Material.LAVA);
|
|
}
|
|
|
|
public void moveRelative(float strafe, float up, float forward, float friction)
|
|
{
|
|
float f = strafe * strafe + up * up + forward * forward;
|
|
|
|
if (f >= 1.0E-4F)
|
|
{
|
|
f = MathHelper.sqrt(f);
|
|
|
|
if (f < 1.0F)
|
|
{
|
|
f = 1.0F;
|
|
}
|
|
|
|
f = friction / f;
|
|
strafe = strafe * f;
|
|
up = up * f;
|
|
forward = forward * f;
|
|
float f1 = MathHelper.sin(this.rotationYaw * 0.017453292F);
|
|
float f2 = MathHelper.cos(this.rotationYaw * 0.017453292F);
|
|
this.motionX += (double)(strafe * f2 - forward * f1);
|
|
this.motionY += (double)up;
|
|
this.motionZ += (double)(forward * f2 + strafe * f1);
|
|
}
|
|
}
|
|
|
|
@SideOnly(Side.CLIENT)
|
|
public int getBrightnessForRender()
|
|
{
|
|
BlockPos.MutableBlockPos blockpos$mutableblockpos = new BlockPos.MutableBlockPos(MathHelper.floor(this.posX), 0, MathHelper.floor(this.posZ));
|
|
|
|
if (this.world.isBlockLoaded(blockpos$mutableblockpos))
|
|
{
|
|
blockpos$mutableblockpos.setY(MathHelper.floor(this.posY + (double)this.getEyeHeight()));
|
|
return this.world.getCombinedLight(blockpos$mutableblockpos, 0);
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets how bright this entity is.
|
|
*/
|
|
public float getBrightness()
|
|
{
|
|
BlockPos.MutableBlockPos blockpos$mutableblockpos = new BlockPos.MutableBlockPos(MathHelper.floor(this.posX), 0, MathHelper.floor(this.posZ));
|
|
|
|
if (this.world.isBlockLoaded(blockpos$mutableblockpos))
|
|
{
|
|
blockpos$mutableblockpos.setY(MathHelper.floor(this.posY + (double)this.getEyeHeight()));
|
|
return this.world.getLightBrightness(blockpos$mutableblockpos);
|
|
}
|
|
else
|
|
{
|
|
return 0.0F;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets the reference to the World object.
|
|
*/
|
|
public void setWorld(World worldIn)
|
|
{
|
|
this.world = worldIn;
|
|
}
|
|
|
|
/**
|
|
* Sets position and rotation, clamping and wrapping params to valid values. Used by network code.
|
|
*/
|
|
public void setPositionAndRotation(double x, double y, double z, float yaw, float pitch)
|
|
{
|
|
this.posX = MathHelper.clamp(x, -3.0E7D, 3.0E7D);
|
|
this.posY = y;
|
|
this.posZ = MathHelper.clamp(z, -3.0E7D, 3.0E7D);
|
|
this.prevPosX = this.posX;
|
|
this.prevPosY = this.posY;
|
|
this.prevPosZ = this.posZ;
|
|
pitch = MathHelper.clamp(pitch, -90.0F, 90.0F);
|
|
this.rotationYaw = yaw;
|
|
this.rotationPitch = pitch;
|
|
this.prevRotationYaw = this.rotationYaw;
|
|
this.prevRotationPitch = this.rotationPitch;
|
|
double d0 = (double)(this.prevRotationYaw - yaw);
|
|
|
|
if (d0 < -180.0D)
|
|
{
|
|
this.prevRotationYaw += 360.0F;
|
|
}
|
|
|
|
if (d0 >= 180.0D)
|
|
{
|
|
this.prevRotationYaw -= 360.0F;
|
|
}
|
|
|
|
this.setPosition(this.posX, this.posY, this.posZ);
|
|
this.setRotation(yaw, pitch);
|
|
}
|
|
|
|
public void moveToBlockPosAndAngles(BlockPos pos, float rotationYawIn, float rotationPitchIn)
|
|
{
|
|
this.setLocationAndAngles((double)pos.getX() + 0.5D, (double)pos.getY(), (double)pos.getZ() + 0.5D, rotationYawIn, rotationPitchIn);
|
|
}
|
|
|
|
/**
|
|
* Sets the location and Yaw/Pitch of an entity in the world
|
|
*/
|
|
public void setLocationAndAngles(double x, double y, double z, float yaw, float pitch)
|
|
{
|
|
this.posX = x;
|
|
this.posY = y;
|
|
this.posZ = z;
|
|
this.prevPosX = this.posX;
|
|
this.prevPosY = this.posY;
|
|
this.prevPosZ = this.posZ;
|
|
this.lastTickPosX = this.posX;
|
|
this.lastTickPosY = this.posY;
|
|
this.lastTickPosZ = this.posZ;
|
|
this.rotationYaw = yaw;
|
|
this.rotationPitch = pitch;
|
|
this.setPosition(this.posX, this.posY, this.posZ);
|
|
}
|
|
|
|
/**
|
|
* Returns the distance to the entity.
|
|
*/
|
|
public float getDistance(Entity entityIn)
|
|
{
|
|
float f = (float)(this.posX - entityIn.posX);
|
|
float f1 = (float)(this.posY - entityIn.posY);
|
|
float f2 = (float)(this.posZ - entityIn.posZ);
|
|
return MathHelper.sqrt(f * f + f1 * f1 + f2 * f2);
|
|
}
|
|
|
|
/**
|
|
* Gets the squared distance to the position.
|
|
*/
|
|
public double getDistanceSq(double x, double y, double z)
|
|
{
|
|
double d0 = this.posX - x;
|
|
double d1 = this.posY - y;
|
|
double d2 = this.posZ - z;
|
|
return d0 * d0 + d1 * d1 + d2 * d2;
|
|
}
|
|
|
|
public double getDistanceSq(BlockPos pos)
|
|
{
|
|
return pos.distanceSq(this.posX, this.posY, this.posZ);
|
|
}
|
|
|
|
public double getDistanceSqToCenter(BlockPos pos)
|
|
{
|
|
return pos.distanceSqToCenter(this.posX, this.posY, this.posZ);
|
|
}
|
|
|
|
/**
|
|
* Gets the distance to the position.
|
|
*/
|
|
public double getDistance(double x, double y, double z)
|
|
{
|
|
double d0 = this.posX - x;
|
|
double d1 = this.posY - y;
|
|
double d2 = this.posZ - z;
|
|
return (double)MathHelper.sqrt(d0 * d0 + d1 * d1 + d2 * d2);
|
|
}
|
|
|
|
/**
|
|
* Returns the squared distance to the entity.
|
|
*/
|
|
public double getDistanceSq(Entity entityIn)
|
|
{
|
|
double d0 = this.posX - entityIn.posX;
|
|
double d1 = this.posY - entityIn.posY;
|
|
double d2 = this.posZ - entityIn.posZ;
|
|
return d0 * d0 + d1 * d1 + d2 * d2;
|
|
}
|
|
|
|
/**
|
|
* Called by a player entity when they collide with an entity
|
|
*/
|
|
public void onCollideWithPlayer(EntityPlayer entityIn)
|
|
{
|
|
}
|
|
|
|
/**
|
|
* Applies a velocity to the entities, to push them away from eachother.
|
|
*/
|
|
public void applyEntityCollision(Entity entityIn)
|
|
{
|
|
if (!this.isRidingSameEntity(entityIn))
|
|
{
|
|
if (!entityIn.noClip && !this.noClip)
|
|
{
|
|
double d0 = entityIn.posX - this.posX;
|
|
double d1 = entityIn.posZ - this.posZ;
|
|
double d2 = MathHelper.absMax(d0, d1);
|
|
|
|
if (d2 >= 0.009999999776482582D)
|
|
{
|
|
d2 = (double)MathHelper.sqrt(d2);
|
|
d0 = d0 / d2;
|
|
d1 = d1 / d2;
|
|
double d3 = 1.0D / d2;
|
|
|
|
if (d3 > 1.0D)
|
|
{
|
|
d3 = 1.0D;
|
|
}
|
|
|
|
d0 = d0 * d3;
|
|
d1 = d1 * d3;
|
|
d0 = d0 * 0.05000000074505806D;
|
|
d1 = d1 * 0.05000000074505806D;
|
|
d0 = d0 * (double)(1.0F - this.entityCollisionReduction);
|
|
d1 = d1 * (double)(1.0F - this.entityCollisionReduction);
|
|
|
|
if (!this.isBeingRidden())
|
|
{
|
|
this.addVelocity(-d0, 0.0D, -d1);
|
|
}
|
|
|
|
if (!entityIn.isBeingRidden())
|
|
{
|
|
entityIn.addVelocity(d0, 0.0D, d1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Adds to the current velocity of the entity, and sets {@link #isAirBorne} to true.
|
|
*/
|
|
public void addVelocity(double x, double y, double z)
|
|
{
|
|
this.motionX += x;
|
|
this.motionY += y;
|
|
this.motionZ += z;
|
|
this.isAirBorne = true;
|
|
}
|
|
|
|
/**
|
|
* Marks this entity's velocity as changed, so that it can be re-synced with the client later
|
|
*/
|
|
protected void markVelocityChanged()
|
|
{
|
|
this.velocityChanged = true;
|
|
}
|
|
|
|
/**
|
|
* Called when the entity is attacked.
|
|
*/
|
|
public boolean attackEntityFrom(DamageSource source, float amount)
|
|
{
|
|
if (this.isEntityInvulnerable(source))
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
this.markVelocityChanged();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* interpolated look vector
|
|
*/
|
|
public Vec3d getLook(float partialTicks)
|
|
{
|
|
if (partialTicks == 1.0F)
|
|
{
|
|
return this.getVectorForRotation(this.rotationPitch, this.rotationYaw);
|
|
}
|
|
else
|
|
{
|
|
float f = this.prevRotationPitch + (this.rotationPitch - this.prevRotationPitch) * partialTicks;
|
|
float f1 = this.prevRotationYaw + (this.rotationYaw - this.prevRotationYaw) * partialTicks;
|
|
return this.getVectorForRotation(f, f1);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Creates a Vec3 using the pitch and yaw of the entities rotation.
|
|
*/
|
|
protected final Vec3d getVectorForRotation(float pitch, float yaw)
|
|
{
|
|
float f = MathHelper.cos(-yaw * 0.017453292F - (float)Math.PI);
|
|
float f1 = MathHelper.sin(-yaw * 0.017453292F - (float)Math.PI);
|
|
float f2 = -MathHelper.cos(-pitch * 0.017453292F);
|
|
float f3 = MathHelper.sin(-pitch * 0.017453292F);
|
|
return new Vec3d((double)(f1 * f2), (double)f3, (double)(f * f2));
|
|
}
|
|
|
|
public Vec3d getPositionEyes(float partialTicks)
|
|
{
|
|
if (partialTicks == 1.0F)
|
|
{
|
|
return new Vec3d(this.posX, this.posY + (double)this.getEyeHeight(), this.posZ);
|
|
}
|
|
else
|
|
{
|
|
double d0 = this.prevPosX + (this.posX - this.prevPosX) * (double)partialTicks;
|
|
double d1 = this.prevPosY + (this.posY - this.prevPosY) * (double)partialTicks + (double)this.getEyeHeight();
|
|
double d2 = this.prevPosZ + (this.posZ - this.prevPosZ) * (double)partialTicks;
|
|
return new Vec3d(d0, d1, d2);
|
|
}
|
|
}
|
|
|
|
@Nullable
|
|
@SideOnly(Side.CLIENT)
|
|
public RayTraceResult rayTrace(double blockReachDistance, float partialTicks)
|
|
{
|
|
Vec3d vec3d = this.getPositionEyes(partialTicks);
|
|
Vec3d vec3d1 = this.getLook(partialTicks);
|
|
Vec3d vec3d2 = vec3d.addVector(vec3d1.x * blockReachDistance, vec3d1.y * blockReachDistance, vec3d1.z * blockReachDistance);
|
|
return this.world.rayTraceBlocks(vec3d, vec3d2, false, false, true);
|
|
}
|
|
|
|
/**
|
|
* Returns true if other Entities should be prevented from moving through this Entity.
|
|
*/
|
|
public boolean canBeCollidedWith()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Returns true if this entity should push and be pushed by other entities when colliding.
|
|
*/
|
|
public boolean canBePushed()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
public void awardKillScore(Entity p_191956_1_, int p_191956_2_, DamageSource p_191956_3_)
|
|
{
|
|
if (p_191956_1_ instanceof EntityPlayerMP)
|
|
{
|
|
CriteriaTriggers.ENTITY_KILLED_PLAYER.trigger((EntityPlayerMP)p_191956_1_, this, p_191956_3_);
|
|
}
|
|
}
|
|
|
|
@SideOnly(Side.CLIENT)
|
|
public boolean isInRangeToRender3d(double x, double y, double z)
|
|
{
|
|
double d0 = this.posX - x;
|
|
double d1 = this.posY - y;
|
|
double d2 = this.posZ - z;
|
|
double d3 = d0 * d0 + d1 * d1 + d2 * d2;
|
|
return this.isInRangeToRenderDist(d3);
|
|
}
|
|
|
|
/**
|
|
* Checks if the entity is in range to render.
|
|
*/
|
|
@SideOnly(Side.CLIENT)
|
|
public boolean isInRangeToRenderDist(double distance)
|
|
{
|
|
double d0 = this.getEntityBoundingBox().getAverageEdgeLength();
|
|
|
|
if (Double.isNaN(d0))
|
|
{
|
|
d0 = 1.0D;
|
|
}
|
|
|
|
d0 = d0 * 64.0D * renderDistanceWeight;
|
|
return distance < d0 * d0;
|
|
}
|
|
|
|
/**
|
|
* Attempts to write this Entity to the given NBTTagCompound. Returns false if the entity is dead or its string
|
|
* representation is null. In this event, the given NBTTagCompound is not modified.
|
|
*
|
|
* Similar to writeToNBTOptional, but does not check whether this Entity is a passenger of another.
|
|
*/
|
|
public boolean writeToNBTAtomically(NBTTagCompound compound)
|
|
{
|
|
String s = this.getEntityString();
|
|
|
|
if (!this.isDead && s != null)
|
|
{
|
|
compound.setString("id", s);
|
|
this.writeToNBT(compound);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Either write this entity to the NBT tag given and return true, or return false without doing anything. If this
|
|
* returns false the entity is not saved on disk. Riding entities return false here as they are saved with their
|
|
* mount.
|
|
*/
|
|
public boolean writeToNBTOptional(NBTTagCompound compound)
|
|
{
|
|
String s = this.getEntityString();
|
|
|
|
if (!this.isDead && s != null && !this.isRiding())
|
|
{
|
|
compound.setString("id", s);
|
|
this.writeToNBT(compound);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public static void registerFixes(DataFixer fixer)
|
|
{
|
|
fixer.registerWalker(FixTypes.ENTITY, new IDataWalker()
|
|
{
|
|
public NBTTagCompound process(IDataFixer fixer, NBTTagCompound compound, int versionIn)
|
|
{
|
|
if (compound.hasKey("Passengers", 9))
|
|
{
|
|
NBTTagList nbttaglist = compound.getTagList("Passengers", 10);
|
|
|
|
for (int i = 0; i < nbttaglist.tagCount(); ++i)
|
|
{
|
|
nbttaglist.set(i, fixer.process(FixTypes.ENTITY, nbttaglist.getCompoundTagAt(i), versionIn));
|
|
}
|
|
}
|
|
|
|
return compound;
|
|
}
|
|
});
|
|
}
|
|
|
|
public NBTTagCompound writeToNBT(NBTTagCompound compound)
|
|
{
|
|
try
|
|
{
|
|
compound.setTag("Pos", this.newDoubleNBTList(this.posX, this.posY, this.posZ));
|
|
compound.setTag("Motion", this.newDoubleNBTList(this.motionX, this.motionY, this.motionZ));
|
|
compound.setTag("Rotation", this.newFloatNBTList(this.rotationYaw, this.rotationPitch));
|
|
compound.setFloat("FallDistance", this.fallDistance);
|
|
compound.setShort("Fire", (short)this.fire);
|
|
compound.setShort("Air", (short)this.getAir());
|
|
compound.setBoolean("OnGround", this.onGround);
|
|
compound.setInteger("Dimension", this.dimension);
|
|
compound.setBoolean("Invulnerable", this.invulnerable);
|
|
compound.setInteger("PortalCooldown", this.timeUntilPortal);
|
|
compound.setUniqueId("UUID", this.getUniqueID());
|
|
|
|
if (this.hasCustomName())
|
|
{
|
|
compound.setString("CustomName", this.getCustomNameTag());
|
|
}
|
|
|
|
if (this.getAlwaysRenderNameTag())
|
|
{
|
|
compound.setBoolean("CustomNameVisible", this.getAlwaysRenderNameTag());
|
|
}
|
|
|
|
this.cmdResultStats.writeStatsToNBT(compound);
|
|
|
|
if (this.isSilent())
|
|
{
|
|
compound.setBoolean("Silent", this.isSilent());
|
|
}
|
|
|
|
if (this.hasNoGravity())
|
|
{
|
|
compound.setBoolean("NoGravity", this.hasNoGravity());
|
|
}
|
|
|
|
if (this.glowing)
|
|
{
|
|
compound.setBoolean("Glowing", this.glowing);
|
|
}
|
|
compound.setBoolean("UpdateBlocked", updateBlocked);
|
|
|
|
if (!this.tags.isEmpty())
|
|
{
|
|
NBTTagList nbttaglist = new NBTTagList();
|
|
|
|
for (String s : this.tags)
|
|
{
|
|
nbttaglist.appendTag(new NBTTagString(s));
|
|
}
|
|
|
|
compound.setTag("Tags", nbttaglist);
|
|
}
|
|
|
|
if (customEntityData != null) compound.setTag("ForgeData", customEntityData);
|
|
if (this.capabilities != null) compound.setTag("ForgeCaps", this.capabilities.serializeNBT());
|
|
|
|
this.writeEntityToNBT(compound);
|
|
|
|
if (this.isBeingRidden())
|
|
{
|
|
NBTTagList nbttaglist1 = new NBTTagList();
|
|
|
|
for (Entity entity : this.getPassengers())
|
|
{
|
|
NBTTagCompound nbttagcompound = new NBTTagCompound();
|
|
|
|
if (entity.writeToNBTAtomically(nbttagcompound))
|
|
{
|
|
nbttaglist1.appendTag(nbttagcompound);
|
|
}
|
|
}
|
|
|
|
if (!nbttaglist1.hasNoTags())
|
|
{
|
|
compound.setTag("Passengers", nbttaglist1);
|
|
}
|
|
}
|
|
|
|
return compound;
|
|
}
|
|
catch (Throwable throwable)
|
|
{
|
|
CrashReport crashreport = CrashReport.makeCrashReport(throwable, "Saving entity NBT");
|
|
CrashReportCategory crashreportcategory = crashreport.makeCategory("Entity being saved");
|
|
this.addEntityCrashInfo(crashreportcategory);
|
|
throw new ReportedException(crashreport);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Reads the entity from NBT (calls an abstract helper method to read specialized data)
|
|
*/
|
|
public void readFromNBT(NBTTagCompound compound)
|
|
{
|
|
try
|
|
{
|
|
NBTTagList nbttaglist = compound.getTagList("Pos", 6);
|
|
NBTTagList nbttaglist2 = compound.getTagList("Motion", 6);
|
|
NBTTagList nbttaglist3 = compound.getTagList("Rotation", 5);
|
|
this.motionX = nbttaglist2.getDoubleAt(0);
|
|
this.motionY = nbttaglist2.getDoubleAt(1);
|
|
this.motionZ = nbttaglist2.getDoubleAt(2);
|
|
|
|
if (Math.abs(this.motionX) > 10.0D)
|
|
{
|
|
this.motionX = 0.0D;
|
|
}
|
|
|
|
if (Math.abs(this.motionY) > 10.0D)
|
|
{
|
|
this.motionY = 0.0D;
|
|
}
|
|
|
|
if (Math.abs(this.motionZ) > 10.0D)
|
|
{
|
|
this.motionZ = 0.0D;
|
|
}
|
|
|
|
this.posX = nbttaglist.getDoubleAt(0);
|
|
this.posY = nbttaglist.getDoubleAt(1);
|
|
this.posZ = nbttaglist.getDoubleAt(2);
|
|
this.lastTickPosX = this.posX;
|
|
this.lastTickPosY = this.posY;
|
|
this.lastTickPosZ = this.posZ;
|
|
this.prevPosX = this.posX;
|
|
this.prevPosY = this.posY;
|
|
this.prevPosZ = this.posZ;
|
|
this.rotationYaw = nbttaglist3.getFloatAt(0);
|
|
this.rotationPitch = nbttaglist3.getFloatAt(1);
|
|
this.prevRotationYaw = this.rotationYaw;
|
|
this.prevRotationPitch = this.rotationPitch;
|
|
this.setRotationYawHead(this.rotationYaw);
|
|
this.setRenderYawOffset(this.rotationYaw);
|
|
this.fallDistance = compound.getFloat("FallDistance");
|
|
this.fire = compound.getShort("Fire");
|
|
this.setAir(compound.getShort("Air"));
|
|
this.onGround = compound.getBoolean("OnGround");
|
|
|
|
if (compound.hasKey("Dimension"))
|
|
{
|
|
this.dimension = compound.getInteger("Dimension");
|
|
}
|
|
|
|
this.invulnerable = compound.getBoolean("Invulnerable");
|
|
this.timeUntilPortal = compound.getInteger("PortalCooldown");
|
|
|
|
if (compound.hasUniqueId("UUID"))
|
|
{
|
|
this.entityUniqueID = compound.getUniqueId("UUID");
|
|
this.cachedUniqueIdString = this.entityUniqueID.toString();
|
|
}
|
|
|
|
this.setPosition(this.posX, this.posY, this.posZ);
|
|
this.setRotation(this.rotationYaw, this.rotationPitch);
|
|
|
|
if (compound.hasKey("CustomName", 8))
|
|
{
|
|
this.setCustomNameTag(compound.getString("CustomName"));
|
|
}
|
|
|
|
this.setAlwaysRenderNameTag(compound.getBoolean("CustomNameVisible"));
|
|
this.cmdResultStats.readStatsFromNBT(compound);
|
|
this.setSilent(compound.getBoolean("Silent"));
|
|
this.setNoGravity(compound.getBoolean("NoGravity"));
|
|
this.setGlowing(compound.getBoolean("Glowing"));
|
|
updateBlocked = compound.getBoolean("UpdateBlocked");
|
|
|
|
if (compound.hasKey("ForgeData")) customEntityData = compound.getCompoundTag("ForgeData");
|
|
if (this.capabilities != null && compound.hasKey("ForgeCaps")) this.capabilities.deserializeNBT(compound.getCompoundTag("ForgeCaps"));
|
|
|
|
if (compound.hasKey("Tags", 9))
|
|
{
|
|
this.tags.clear();
|
|
NBTTagList nbttaglist1 = compound.getTagList("Tags", 8);
|
|
int i = Math.min(nbttaglist1.tagCount(), 1024);
|
|
|
|
for (int j = 0; j < i; ++j)
|
|
{
|
|
this.tags.add(nbttaglist1.getStringTagAt(j));
|
|
}
|
|
}
|
|
|
|
this.readEntityFromNBT(compound);
|
|
|
|
if (this.shouldSetPosAfterLoading())
|
|
{
|
|
this.setPosition(this.posX, this.posY, this.posZ);
|
|
}
|
|
}
|
|
catch (Throwable throwable)
|
|
{
|
|
CrashReport crashreport = CrashReport.makeCrashReport(throwable, "Loading entity NBT");
|
|
CrashReportCategory crashreportcategory = crashreport.makeCategory("Entity being loaded");
|
|
this.addEntityCrashInfo(crashreportcategory);
|
|
throw new ReportedException(crashreport);
|
|
}
|
|
}
|
|
|
|
protected boolean shouldSetPosAfterLoading()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Returns the string that identifies this Entity's class
|
|
*/
|
|
@Nullable
|
|
protected final String getEntityString()
|
|
{
|
|
ResourceLocation resourcelocation = EntityList.getKey(this);
|
|
return resourcelocation == null ? null : resourcelocation.toString();
|
|
}
|
|
|
|
/**
|
|
* (abstract) Protected helper method to read subclass entity data from NBT.
|
|
*/
|
|
protected abstract void readEntityFromNBT(NBTTagCompound compound);
|
|
|
|
/**
|
|
* (abstract) Protected helper method to write subclass entity data to NBT.
|
|
*/
|
|
protected abstract void writeEntityToNBT(NBTTagCompound compound);
|
|
|
|
/**
|
|
* creates a NBT list from the array of doubles passed to this function
|
|
*/
|
|
protected NBTTagList newDoubleNBTList(double... numbers)
|
|
{
|
|
NBTTagList nbttaglist = new NBTTagList();
|
|
|
|
for (double d0 : numbers)
|
|
{
|
|
nbttaglist.appendTag(new NBTTagDouble(d0));
|
|
}
|
|
|
|
return nbttaglist;
|
|
}
|
|
|
|
/**
|
|
* Returns a new NBTTagList filled with the specified floats
|
|
*/
|
|
protected NBTTagList newFloatNBTList(float... numbers)
|
|
{
|
|
NBTTagList nbttaglist = new NBTTagList();
|
|
|
|
for (float f : numbers)
|
|
{
|
|
nbttaglist.appendTag(new NBTTagFloat(f));
|
|
}
|
|
|
|
return nbttaglist;
|
|
}
|
|
|
|
@Nullable
|
|
public EntityItem dropItem(Item itemIn, int size)
|
|
{
|
|
return this.dropItemWithOffset(itemIn, size, 0.0F);
|
|
}
|
|
|
|
@Nullable
|
|
public EntityItem dropItemWithOffset(Item itemIn, int size, float offsetY)
|
|
{
|
|
return this.entityDropItem(new ItemStack(itemIn, size, 0), offsetY);
|
|
}
|
|
|
|
/**
|
|
* Drops an item at the position of the entity.
|
|
*/
|
|
@Nullable
|
|
public EntityItem entityDropItem(ItemStack stack, float offsetY)
|
|
{
|
|
if (stack.isEmpty())
|
|
{
|
|
return null;
|
|
}
|
|
else
|
|
{
|
|
EntityItem entityitem = new EntityItem(this.world, this.posX, this.posY + (double)offsetY, this.posZ, stack);
|
|
entityitem.setDefaultPickupDelay();
|
|
if (captureDrops)
|
|
this.capturedDrops.add(entityitem);
|
|
else
|
|
this.world.spawnEntity(entityitem);
|
|
return entityitem;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks whether target entity is alive.
|
|
*/
|
|
public boolean isEntityAlive()
|
|
{
|
|
return !this.isDead;
|
|
}
|
|
|
|
/**
|
|
* Checks if this entity is inside of an opaque block
|
|
*/
|
|
public boolean isEntityInsideOpaqueBlock()
|
|
{
|
|
if (this.noClip)
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
BlockPos.PooledMutableBlockPos blockpos$pooledmutableblockpos = BlockPos.PooledMutableBlockPos.retain();
|
|
|
|
for (int i = 0; i < 8; ++i)
|
|
{
|
|
int j = MathHelper.floor(this.posY + (double)(((float)((i >> 0) % 2) - 0.5F) * 0.1F) + (double)this.getEyeHeight());
|
|
int k = MathHelper.floor(this.posX + (double)(((float)((i >> 1) % 2) - 0.5F) * this.width * 0.8F));
|
|
int l = MathHelper.floor(this.posZ + (double)(((float)((i >> 2) % 2) - 0.5F) * this.width * 0.8F));
|
|
|
|
if (blockpos$pooledmutableblockpos.getX() != k || blockpos$pooledmutableblockpos.getY() != j || blockpos$pooledmutableblockpos.getZ() != l)
|
|
{
|
|
blockpos$pooledmutableblockpos.setPos(k, j, l);
|
|
|
|
if (this.world.getBlockState(blockpos$pooledmutableblockpos).causesSuffocation())
|
|
{
|
|
blockpos$pooledmutableblockpos.release();
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
blockpos$pooledmutableblockpos.release();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public boolean processInitialInteract(EntityPlayer player, EnumHand hand)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Returns a boundingBox used to collide the entity with other entities and blocks. This enables the entity to be
|
|
* pushable on contact, like boats or minecarts.
|
|
*/
|
|
@Nullable
|
|
public AxisAlignedBB getCollisionBox(Entity entityIn)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Handles updating while riding another entity
|
|
*/
|
|
public void updateRidden()
|
|
{
|
|
Entity entity = this.getRidingEntity();
|
|
|
|
if (this.isRiding() && entity.isDead)
|
|
{
|
|
this.dismountRidingEntity();
|
|
}
|
|
else
|
|
{
|
|
this.motionX = 0.0D;
|
|
this.motionY = 0.0D;
|
|
this.motionZ = 0.0D;
|
|
if(!updateBlocked)
|
|
this.onUpdate();
|
|
|
|
if (this.isRiding())
|
|
{
|
|
entity.updatePassenger(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void updatePassenger(Entity passenger)
|
|
{
|
|
if (this.isPassenger(passenger))
|
|
{
|
|
passenger.setPosition(this.posX, this.posY + this.getMountedYOffset() + passenger.getYOffset(), this.posZ);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Applies this entity's orientation (pitch/yaw) to another entity. Used to update passenger orientation.
|
|
*/
|
|
@SideOnly(Side.CLIENT)
|
|
public void applyOrientationToEntity(Entity entityToUpdate)
|
|
{
|
|
}
|
|
|
|
/**
|
|
* Returns the Y Offset of this entity.
|
|
*/
|
|
public double getYOffset()
|
|
{
|
|
return 0.0D;
|
|
}
|
|
|
|
/**
|
|
* Returns the Y offset from the entity's position for any entity riding this one.
|
|
*/
|
|
public double getMountedYOffset()
|
|
{
|
|
return (double)this.height * 0.75D;
|
|
}
|
|
|
|
public boolean startRiding(Entity entityIn)
|
|
{
|
|
return this.startRiding(entityIn, false);
|
|
}
|
|
|
|
public boolean startRiding(Entity entityIn, boolean force)
|
|
{
|
|
for (Entity entity = entityIn; entity.ridingEntity != null; entity = entity.ridingEntity)
|
|
{
|
|
if (entity.ridingEntity == this)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!net.minecraftforge.event.ForgeEventFactory.canMountEntity(this, entityIn, true)) return false;
|
|
if (force || this.canBeRidden(entityIn) && entityIn.canFitPassenger(this))
|
|
{
|
|
if (this.isRiding())
|
|
{
|
|
this.dismountRidingEntity();
|
|
}
|
|
|
|
this.ridingEntity = entityIn;
|
|
this.ridingEntity.addPassenger(this);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
protected boolean canBeRidden(Entity entityIn)
|
|
{
|
|
return this.rideCooldown <= 0;
|
|
}
|
|
|
|
/**
|
|
* Dismounts all entities riding this entity from this entity.
|
|
*/
|
|
public void removePassengers()
|
|
{
|
|
for (int i = this.riddenByEntities.size() - 1; i >= 0; --i)
|
|
{
|
|
((Entity)this.riddenByEntities.get(i)).dismountRidingEntity();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Dismounts this entity from the entity it is riding.
|
|
*/
|
|
public void dismountRidingEntity()
|
|
{
|
|
if (this.ridingEntity != null)
|
|
{
|
|
Entity entity = this.ridingEntity;
|
|
if (!net.minecraftforge.event.ForgeEventFactory.canMountEntity(this, entity, false)) return;
|
|
this.ridingEntity = null;
|
|
entity.removePassenger(this);
|
|
}
|
|
}
|
|
|
|
protected void addPassenger(Entity passenger)
|
|
{
|
|
if (passenger.getRidingEntity() != this)
|
|
{
|
|
throw new IllegalStateException("Use x.startRiding(y), not y.addPassenger(x)");
|
|
}
|
|
else
|
|
{
|
|
if (!this.world.isRemote && passenger instanceof EntityPlayer && !(this.getControllingPassenger() instanceof EntityPlayer))
|
|
{
|
|
this.riddenByEntities.add(0, passenger);
|
|
}
|
|
else
|
|
{
|
|
this.riddenByEntities.add(passenger);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected void removePassenger(Entity passenger)
|
|
{
|
|
if (passenger.getRidingEntity() == this)
|
|
{
|
|
throw new IllegalStateException("Use x.stopRiding(y), not y.removePassenger(x)");
|
|
}
|
|
else
|
|
{
|
|
this.riddenByEntities.remove(passenger);
|
|
passenger.rideCooldown = 60;
|
|
}
|
|
}
|
|
|
|
protected boolean canFitPassenger(Entity passenger)
|
|
{
|
|
return this.getPassengers().size() < 1;
|
|
}
|
|
|
|
/**
|
|
* Set the position and rotation values directly without any clamping.
|
|
*/
|
|
@SideOnly(Side.CLIENT)
|
|
public void setPositionAndRotationDirect(double x, double y, double z, float yaw, float pitch, int posRotationIncrements, boolean teleport)
|
|
{
|
|
this.setPosition(x, y, z);
|
|
this.setRotation(yaw, pitch);
|
|
}
|
|
|
|
public float getCollisionBorderSize()
|
|
{
|
|
return 0.0F;
|
|
}
|
|
|
|
/**
|
|
* returns a (normalized) vector of where this entity is looking
|
|
*/
|
|
public Vec3d getLookVec()
|
|
{
|
|
return this.getVectorForRotation(this.rotationPitch, this.rotationYaw);
|
|
}
|
|
|
|
/**
|
|
* returns the Entity's pitch and yaw as a Vec2f
|
|
*/
|
|
@SideOnly(Side.CLIENT)
|
|
public Vec2f getPitchYaw()
|
|
{
|
|
return new Vec2f(this.rotationPitch, this.rotationYaw);
|
|
}
|
|
|
|
@SideOnly(Side.CLIENT)
|
|
public Vec3d getForward()
|
|
{
|
|
return Vec3d.fromPitchYawVector(this.getPitchYaw());
|
|
}
|
|
|
|
/**
|
|
* Marks the entity as being inside a portal, activating teleportation logic in onEntityUpdate() in the following
|
|
* tick(s).
|
|
*/
|
|
public void setPortal(BlockPos pos)
|
|
{
|
|
if (this.timeUntilPortal > 0)
|
|
{
|
|
this.timeUntilPortal = this.getPortalCooldown();
|
|
}
|
|
else
|
|
{
|
|
if (!this.world.isRemote && !pos.equals(this.lastPortalPos))
|
|
{
|
|
this.lastPortalPos = new BlockPos(pos);
|
|
BlockPattern.PatternHelper blockpattern$patternhelper = Blocks.PORTAL.createPatternHelper(this.world, this.lastPortalPos);
|
|
double d0 = blockpattern$patternhelper.getForwards().getAxis() == EnumFacing.Axis.X ? (double)blockpattern$patternhelper.getFrontTopLeft().getZ() : (double)blockpattern$patternhelper.getFrontTopLeft().getX();
|
|
double d1 = blockpattern$patternhelper.getForwards().getAxis() == EnumFacing.Axis.X ? this.posZ : this.posX;
|
|
d1 = Math.abs(MathHelper.pct(d1 - (double)(blockpattern$patternhelper.getForwards().rotateY().getAxisDirection() == EnumFacing.AxisDirection.NEGATIVE ? 1 : 0), d0, d0 - (double)blockpattern$patternhelper.getWidth()));
|
|
double d2 = MathHelper.pct(this.posY - 1.0D, (double)blockpattern$patternhelper.getFrontTopLeft().getY(), (double)(blockpattern$patternhelper.getFrontTopLeft().getY() - blockpattern$patternhelper.getHeight()));
|
|
this.lastPortalVec = new Vec3d(d1, d2, 0.0D);
|
|
this.teleportDirection = blockpattern$patternhelper.getForwards();
|
|
}
|
|
|
|
this.inPortal = true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return the amount of cooldown before this entity can use a portal again.
|
|
*/
|
|
public int getPortalCooldown()
|
|
{
|
|
return 300;
|
|
}
|
|
|
|
/**
|
|
* Updates the entity motion clientside, called by packets from the server
|
|
*/
|
|
@SideOnly(Side.CLIENT)
|
|
public void setVelocity(double x, double y, double z)
|
|
{
|
|
this.motionX = x;
|
|
this.motionY = y;
|
|
this.motionZ = z;
|
|
}
|
|
|
|
/**
|
|
* Handler for {@link World#setEntityState}
|
|
*/
|
|
@SideOnly(Side.CLIENT)
|
|
public void handleStatusUpdate(byte id)
|
|
{
|
|
}
|
|
|
|
/**
|
|
* Setups the entity to do the hurt animation. Only used by packets in multiplayer.
|
|
*/
|
|
@SideOnly(Side.CLIENT)
|
|
public void performHurtAnimation()
|
|
{
|
|
}
|
|
|
|
public Iterable<ItemStack> getHeldEquipment()
|
|
{
|
|
return EMPTY_EQUIPMENT;
|
|
}
|
|
|
|
public Iterable<ItemStack> getArmorInventoryList()
|
|
{
|
|
return EMPTY_EQUIPMENT;
|
|
}
|
|
|
|
public Iterable<ItemStack> getEquipmentAndArmor()
|
|
{
|
|
return Iterables.<ItemStack>concat(this.getHeldEquipment(), this.getArmorInventoryList());
|
|
}
|
|
|
|
public void setItemStackToSlot(EntityEquipmentSlot slotIn, ItemStack stack)
|
|
{
|
|
}
|
|
|
|
/**
|
|
* Returns true if the entity is on fire. Used by render to add the fire effect on rendering.
|
|
*/
|
|
public boolean isBurning()
|
|
{
|
|
boolean flag = this.world != null && this.world.isRemote;
|
|
return !this.isImmuneToFire && (this.fire > 0 || flag && this.getFlag(0));
|
|
}
|
|
|
|
public boolean isRiding()
|
|
{
|
|
return this.getRidingEntity() != null;
|
|
}
|
|
|
|
/**
|
|
* If at least 1 entity is riding this one
|
|
*/
|
|
public boolean isBeingRidden()
|
|
{
|
|
return !this.getPassengers().isEmpty();
|
|
}
|
|
|
|
/**
|
|
* Returns if this entity is sneaking.
|
|
*/
|
|
public boolean isSneaking()
|
|
{
|
|
return this.getFlag(1);
|
|
}
|
|
|
|
/**
|
|
* Sets the sneaking flag.
|
|
*/
|
|
public void setSneaking(boolean sneaking)
|
|
{
|
|
this.setFlag(1, sneaking);
|
|
}
|
|
|
|
/**
|
|
* Get if the Entity is sprinting.
|
|
*/
|
|
public boolean isSprinting()
|
|
{
|
|
return this.getFlag(3);
|
|
}
|
|
|
|
/**
|
|
* Set sprinting switch for Entity.
|
|
*/
|
|
public void setSprinting(boolean sprinting)
|
|
{
|
|
this.setFlag(3, sprinting);
|
|
}
|
|
|
|
public boolean isGlowing()
|
|
{
|
|
return this.glowing || this.world.isRemote && this.getFlag(6);
|
|
}
|
|
|
|
public void setGlowing(boolean glowingIn)
|
|
{
|
|
this.glowing = glowingIn;
|
|
|
|
if (!this.world.isRemote)
|
|
{
|
|
this.setFlag(6, this.glowing);
|
|
}
|
|
}
|
|
|
|
public boolean isInvisible()
|
|
{
|
|
return this.getFlag(5);
|
|
}
|
|
|
|
/**
|
|
* Only used by renderer in EntityLivingBase subclasses.
|
|
* Determines if an entity is visible or not to a specfic player, if the entity is normally invisible.
|
|
* For EntityLivingBase subclasses, returning false when invisible will render the entity semitransparent.
|
|
*/
|
|
@SideOnly(Side.CLIENT)
|
|
public boolean isInvisibleToPlayer(EntityPlayer player)
|
|
{
|
|
if (player.isSpectator())
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
Team team = this.getTeam();
|
|
return team != null && player != null && player.getTeam() == team && team.getSeeFriendlyInvisiblesEnabled() ? false : this.isInvisible();
|
|
}
|
|
}
|
|
|
|
@Nullable
|
|
public Team getTeam()
|
|
{
|
|
return this.world.getScoreboard().getPlayersTeam(this.getCachedUniqueIdString());
|
|
}
|
|
|
|
/**
|
|
* Returns whether this Entity is on the same team as the given Entity.
|
|
*/
|
|
public boolean isOnSameTeam(Entity entityIn)
|
|
{
|
|
return this.isOnScoreboardTeam(entityIn.getTeam());
|
|
}
|
|
|
|
/**
|
|
* Returns whether this Entity is on the given scoreboard team.
|
|
*/
|
|
public boolean isOnScoreboardTeam(Team teamIn)
|
|
{
|
|
return this.getTeam() != null ? this.getTeam().isSameTeam(teamIn) : false;
|
|
}
|
|
|
|
public void setInvisible(boolean invisible)
|
|
{
|
|
this.setFlag(5, invisible);
|
|
}
|
|
|
|
/**
|
|
* Returns true if the flag is active for the entity. Known flags: 0: burning; 1: sneaking; 2: unused; 3: sprinting;
|
|
* 4: unused; 5: invisible; 6: glowing; 7: elytra flying
|
|
*/
|
|
protected boolean getFlag(int flag)
|
|
{
|
|
return (((Byte)this.dataManager.get(FLAGS)).byteValue() & 1 << flag) != 0;
|
|
}
|
|
|
|
/**
|
|
* Enable or disable a entity flag, see getEntityFlag to read the know flags.
|
|
*/
|
|
protected void setFlag(int flag, boolean set)
|
|
{
|
|
byte b0 = ((Byte)this.dataManager.get(FLAGS)).byteValue();
|
|
|
|
if (set)
|
|
{
|
|
this.dataManager.set(FLAGS, Byte.valueOf((byte)(b0 | 1 << flag)));
|
|
}
|
|
else
|
|
{
|
|
this.dataManager.set(FLAGS, Byte.valueOf((byte)(b0 & ~(1 << flag))));
|
|
}
|
|
}
|
|
|
|
public int getAir()
|
|
{
|
|
return ((Integer)this.dataManager.get(AIR)).intValue();
|
|
}
|
|
|
|
public void setAir(int air)
|
|
{
|
|
this.dataManager.set(AIR, Integer.valueOf(air));
|
|
}
|
|
|
|
/**
|
|
* Called when a lightning bolt hits the entity.
|
|
*/
|
|
public void onStruckByLightning(EntityLightningBolt lightningBolt)
|
|
{
|
|
this.attackEntityFrom(DamageSource.LIGHTNING_BOLT, 5.0F);
|
|
++this.fire;
|
|
|
|
if (this.fire == 0)
|
|
{
|
|
this.setFire(8);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This method gets called when the entity kills another one.
|
|
*/
|
|
public void onKillEntity(EntityLivingBase entityLivingIn)
|
|
{
|
|
}
|
|
|
|
protected boolean pushOutOfBlocks(double x, double y, double z)
|
|
{
|
|
BlockPos blockpos = new BlockPos(x, y, z);
|
|
double d0 = x - (double)blockpos.getX();
|
|
double d1 = y - (double)blockpos.getY();
|
|
double d2 = z - (double)blockpos.getZ();
|
|
|
|
if (!this.world.collidesWithAnyBlock(this.getEntityBoundingBox()))
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
EnumFacing enumfacing = EnumFacing.UP;
|
|
double d3 = Double.MAX_VALUE;
|
|
|
|
if (!this.world.isBlockFullCube(blockpos.west()) && d0 < d3)
|
|
{
|
|
d3 = d0;
|
|
enumfacing = EnumFacing.WEST;
|
|
}
|
|
|
|
if (!this.world.isBlockFullCube(blockpos.east()) && 1.0D - d0 < d3)
|
|
{
|
|
d3 = 1.0D - d0;
|
|
enumfacing = EnumFacing.EAST;
|
|
}
|
|
|
|
if (!this.world.isBlockFullCube(blockpos.north()) && d2 < d3)
|
|
{
|
|
d3 = d2;
|
|
enumfacing = EnumFacing.NORTH;
|
|
}
|
|
|
|
if (!this.world.isBlockFullCube(blockpos.south()) && 1.0D - d2 < d3)
|
|
{
|
|
d3 = 1.0D - d2;
|
|
enumfacing = EnumFacing.SOUTH;
|
|
}
|
|
|
|
if (!this.world.isBlockFullCube(blockpos.up()) && 1.0D - d1 < d3)
|
|
{
|
|
d3 = 1.0D - d1;
|
|
enumfacing = EnumFacing.UP;
|
|
}
|
|
|
|
float f = this.rand.nextFloat() * 0.2F + 0.1F;
|
|
float f1 = (float)enumfacing.getAxisDirection().getOffset();
|
|
|
|
if (enumfacing.getAxis() == EnumFacing.Axis.X)
|
|
{
|
|
this.motionX = (double)(f1 * f);
|
|
this.motionY *= 0.75D;
|
|
this.motionZ *= 0.75D;
|
|
}
|
|
else if (enumfacing.getAxis() == EnumFacing.Axis.Y)
|
|
{
|
|
this.motionX *= 0.75D;
|
|
this.motionY = (double)(f1 * f);
|
|
this.motionZ *= 0.75D;
|
|
}
|
|
else if (enumfacing.getAxis() == EnumFacing.Axis.Z)
|
|
{
|
|
this.motionX *= 0.75D;
|
|
this.motionY *= 0.75D;
|
|
this.motionZ = (double)(f1 * f);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets the Entity inside a web block.
|
|
*/
|
|
public void setInWeb()
|
|
{
|
|
this.isInWeb = true;
|
|
this.fallDistance = 0.0F;
|
|
}
|
|
|
|
/**
|
|
* Get the name of this object. For players this returns their username
|
|
*/
|
|
public String getName()
|
|
{
|
|
if (this.hasCustomName())
|
|
{
|
|
return this.getCustomNameTag();
|
|
}
|
|
else
|
|
{
|
|
String s = EntityList.getEntityString(this);
|
|
|
|
if (s == null)
|
|
{
|
|
s = "generic";
|
|
}
|
|
|
|
return I18n.translateToLocal("entity." + s + ".name");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return the Entity parts making up this Entity (currently only for dragons)
|
|
*/
|
|
@Nullable
|
|
public Entity[] getParts()
|
|
{
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Returns true if Entity argument is equal to this Entity
|
|
*/
|
|
public boolean isEntityEqual(Entity entityIn)
|
|
{
|
|
return this == entityIn;
|
|
}
|
|
|
|
public float getRotationYawHead()
|
|
{
|
|
return 0.0F;
|
|
}
|
|
|
|
/**
|
|
* Sets the head's yaw rotation of the entity.
|
|
*/
|
|
public void setRotationYawHead(float rotation)
|
|
{
|
|
}
|
|
|
|
/**
|
|
* Set the render yaw offset
|
|
*/
|
|
public void setRenderYawOffset(float offset)
|
|
{
|
|
}
|
|
|
|
/**
|
|
* Returns true if it's possible to attack this entity with an item.
|
|
*/
|
|
public boolean canBeAttackedWithItem()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Called when a player attacks an entity. If this returns true the attack will not happen.
|
|
*/
|
|
public boolean hitByEntity(Entity entityIn)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
public String toString()
|
|
{
|
|
return String.format("%s['%s'/%d, l='%s', x=%.2f, y=%.2f, z=%.2f]", this.getClass().getSimpleName(), this.getName(), this.entityId, this.world == null ? "~NULL~" : this.world.getWorldInfo().getWorldName(), this.posX, this.posY, this.posZ);
|
|
}
|
|
|
|
/**
|
|
* Returns whether this Entity is invulnerable to the given DamageSource.
|
|
*/
|
|
public boolean isEntityInvulnerable(DamageSource source)
|
|
{
|
|
return this.invulnerable && source != DamageSource.OUT_OF_WORLD && !source.isCreativePlayer();
|
|
}
|
|
|
|
public boolean getIsInvulnerable()
|
|
{
|
|
return this.invulnerable;
|
|
}
|
|
|
|
/**
|
|
* Sets whether this Entity is invulnerable.
|
|
*/
|
|
public void setEntityInvulnerable(boolean isInvulnerable)
|
|
{
|
|
this.invulnerable = isInvulnerable;
|
|
}
|
|
|
|
/**
|
|
* Sets this entity's location and angles to the location and angles of the passed in entity.
|
|
*/
|
|
public void copyLocationAndAnglesFrom(Entity entityIn)
|
|
{
|
|
this.setLocationAndAngles(entityIn.posX, entityIn.posY, entityIn.posZ, entityIn.rotationYaw, entityIn.rotationPitch);
|
|
}
|
|
|
|
/**
|
|
* Prepares this entity in new dimension by copying NBT data from entity in old dimension
|
|
*/
|
|
private void copyDataFromOld(Entity entityIn)
|
|
{
|
|
NBTTagCompound nbttagcompound = entityIn.writeToNBT(new NBTTagCompound());
|
|
nbttagcompound.removeTag("Dimension");
|
|
this.readFromNBT(nbttagcompound);
|
|
this.timeUntilPortal = entityIn.timeUntilPortal;
|
|
this.lastPortalPos = entityIn.lastPortalPos;
|
|
this.lastPortalVec = entityIn.lastPortalVec;
|
|
this.teleportDirection = entityIn.teleportDirection;
|
|
}
|
|
|
|
@Nullable
|
|
public Entity changeDimension(int dimensionIn)
|
|
{
|
|
if (this.world.isRemote || this.isDead) return null;
|
|
return changeDimension(dimensionIn, this.getServer().getWorld(dimensionIn).getDefaultTeleporter());
|
|
}
|
|
|
|
@Nullable // Forge: Entities that require custom handling should override this method, not the other
|
|
public Entity changeDimension(int dimensionIn, net.minecraftforge.common.util.ITeleporter teleporter)
|
|
{
|
|
if (!this.world.isRemote && !this.isDead)
|
|
{
|
|
if (!net.minecraftforge.common.ForgeHooks.onTravelToDimension(this, dimensionIn)) return null;
|
|
this.world.profiler.startSection("changeDimension");
|
|
MinecraftServer minecraftserver = this.getServer();
|
|
int i = this.dimension;
|
|
WorldServer worldserver = minecraftserver.getWorld(i);
|
|
WorldServer worldserver1 = minecraftserver.getWorld(dimensionIn);
|
|
this.dimension = dimensionIn;
|
|
|
|
if (i == 1 && dimensionIn == 1 && teleporter.isVanilla())
|
|
{
|
|
worldserver1 = minecraftserver.getWorld(0);
|
|
this.dimension = 0;
|
|
}
|
|
|
|
this.world.removeEntity(this);
|
|
this.isDead = false;
|
|
this.world.profiler.startSection("reposition");
|
|
BlockPos blockpos;
|
|
|
|
if (dimensionIn == 1 && teleporter.isVanilla())
|
|
{
|
|
blockpos = worldserver1.getSpawnCoordinate();
|
|
}
|
|
else
|
|
{
|
|
double moveFactor = worldserver.provider.getMovementFactor() / worldserver1.provider.getMovementFactor();
|
|
double d0 = MathHelper.clamp(this.posX * moveFactor, worldserver1.getWorldBorder().minX() + 16.0D, worldserver1.getWorldBorder().maxX() - 16.0D);
|
|
double d1 = MathHelper.clamp(this.posZ * moveFactor, worldserver1.getWorldBorder().minZ() + 16.0D, worldserver1.getWorldBorder().maxZ() - 16.0D);
|
|
double d2 = 8.0D;
|
|
|
|
if (false && dimensionIn == -1)
|
|
{
|
|
d0 = MathHelper.clamp(d0 / 8.0D, worldserver1.getWorldBorder().minX() + 16.0D, worldserver1.getWorldBorder().maxX() - 16.0D);
|
|
d1 = MathHelper.clamp(d1 / 8.0D, worldserver1.getWorldBorder().minZ() + 16.0D, worldserver1.getWorldBorder().maxZ() - 16.0D);
|
|
}
|
|
else if (false && dimensionIn == 0)
|
|
{
|
|
d0 = MathHelper.clamp(d0 * 8.0D, worldserver1.getWorldBorder().minX() + 16.0D, worldserver1.getWorldBorder().maxX() - 16.0D);
|
|
d1 = MathHelper.clamp(d1 * 8.0D, worldserver1.getWorldBorder().minZ() + 16.0D, worldserver1.getWorldBorder().maxZ() - 16.0D);
|
|
}
|
|
|
|
d0 = (double)MathHelper.clamp((int)d0, -29999872, 29999872);
|
|
d1 = (double)MathHelper.clamp((int)d1, -29999872, 29999872);
|
|
float f = this.rotationYaw;
|
|
this.setLocationAndAngles(d0, this.posY, d1, 90.0F, 0.0F);
|
|
teleporter.placeEntity(worldserver1, this, f);
|
|
blockpos = new BlockPos(this);
|
|
}
|
|
|
|
worldserver.updateEntityWithOptionalForce(this, false);
|
|
this.world.profiler.endStartSection("reloading");
|
|
Entity entity = EntityList.newEntity(this.getClass(), worldserver1);
|
|
|
|
if (entity != null)
|
|
{
|
|
entity.copyDataFromOld(this);
|
|
|
|
if (i == 1 && dimensionIn == 1 && teleporter.isVanilla())
|
|
{
|
|
BlockPos blockpos1 = worldserver1.getTopSolidOrLiquidBlock(worldserver1.getSpawnPoint());
|
|
entity.moveToBlockPosAndAngles(blockpos1, entity.rotationYaw, entity.rotationPitch);
|
|
}
|
|
else
|
|
{
|
|
entity.moveToBlockPosAndAngles(blockpos, entity.rotationYaw, entity.rotationPitch);
|
|
}
|
|
|
|
boolean flag = entity.forceSpawn;
|
|
entity.forceSpawn = true;
|
|
worldserver1.spawnEntity(entity);
|
|
entity.forceSpawn = flag;
|
|
worldserver1.updateEntityWithOptionalForce(entity, false);
|
|
}
|
|
|
|
this.isDead = true;
|
|
this.world.profiler.endSection();
|
|
worldserver.resetUpdateEntityTick();
|
|
worldserver1.resetUpdateEntityTick();
|
|
this.world.profiler.endSection();
|
|
return entity;
|
|
}
|
|
else
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns false if this Entity is a boss, true otherwise.
|
|
*/
|
|
public boolean isNonBoss()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Explosion resistance of a block relative to this entity
|
|
*/
|
|
public float getExplosionResistance(Explosion explosionIn, World worldIn, BlockPos pos, IBlockState blockStateIn)
|
|
{
|
|
return blockStateIn.getBlock().getExplosionResistance(worldIn, pos, this, explosionIn);
|
|
}
|
|
|
|
public boolean canExplosionDestroyBlock(Explosion explosionIn, World worldIn, BlockPos pos, IBlockState blockStateIn, float p_174816_5_)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* The maximum height from where the entity is alowed to jump (used in pathfinder)
|
|
*/
|
|
public int getMaxFallHeight()
|
|
{
|
|
return 3;
|
|
}
|
|
|
|
public Vec3d getLastPortalVec()
|
|
{
|
|
return this.lastPortalVec;
|
|
}
|
|
|
|
public EnumFacing getTeleportDirection()
|
|
{
|
|
return this.teleportDirection;
|
|
}
|
|
|
|
/**
|
|
* Return whether this entity should NOT trigger a pressure plate or a tripwire.
|
|
*/
|
|
public boolean doesEntityNotTriggerPressurePlate()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
public void addEntityCrashInfo(CrashReportCategory category)
|
|
{
|
|
category.addDetail("Entity Type", new ICrashReportDetail<String>()
|
|
{
|
|
public String call() throws Exception
|
|
{
|
|
return EntityList.getKey(Entity.this) + " (" + Entity.this.getClass().getCanonicalName() + ")";
|
|
}
|
|
});
|
|
category.addCrashSection("Entity ID", Integer.valueOf(this.entityId));
|
|
category.addDetail("Entity Name", new ICrashReportDetail<String>()
|
|
{
|
|
public String call() throws Exception
|
|
{
|
|
return Entity.this.getName();
|
|
}
|
|
});
|
|
category.addCrashSection("Entity's Exact location", String.format("%.2f, %.2f, %.2f", this.posX, this.posY, this.posZ));
|
|
category.addCrashSection("Entity's Block location", CrashReportCategory.getCoordinateInfo(MathHelper.floor(this.posX), MathHelper.floor(this.posY), MathHelper.floor(this.posZ)));
|
|
category.addCrashSection("Entity's Momentum", String.format("%.2f, %.2f, %.2f", this.motionX, this.motionY, this.motionZ));
|
|
category.addDetail("Entity's Passengers", new ICrashReportDetail<String>()
|
|
{
|
|
public String call() throws Exception
|
|
{
|
|
return Entity.this.getPassengers().toString();
|
|
}
|
|
});
|
|
category.addDetail("Entity's Vehicle", new ICrashReportDetail<String>()
|
|
{
|
|
public String call() throws Exception
|
|
{
|
|
return Entity.this.getRidingEntity().toString();
|
|
}
|
|
});
|
|
}
|
|
|
|
public void setUniqueId(UUID uniqueIdIn)
|
|
{
|
|
this.entityUniqueID = uniqueIdIn;
|
|
this.cachedUniqueIdString = this.entityUniqueID.toString();
|
|
}
|
|
|
|
/**
|
|
* Return whether this entity should be rendered as on fire.
|
|
*/
|
|
@SideOnly(Side.CLIENT)
|
|
public boolean canRenderOnFire()
|
|
{
|
|
return this.isBurning();
|
|
}
|
|
|
|
/**
|
|
* Returns the UUID of this entity.
|
|
*/
|
|
public UUID getUniqueID()
|
|
{
|
|
return this.entityUniqueID;
|
|
}
|
|
|
|
public String getCachedUniqueIdString()
|
|
{
|
|
return this.cachedUniqueIdString;
|
|
}
|
|
|
|
public boolean isPushedByWater()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
@SideOnly(Side.CLIENT)
|
|
public static double getRenderDistanceWeight()
|
|
{
|
|
return renderDistanceWeight;
|
|
}
|
|
|
|
@SideOnly(Side.CLIENT)
|
|
public static void setRenderDistanceWeight(double renderDistWeight)
|
|
{
|
|
renderDistanceWeight = renderDistWeight;
|
|
}
|
|
|
|
/**
|
|
* Get the formatted ChatComponent that will be used for the sender's username in chat
|
|
*/
|
|
public ITextComponent getDisplayName()
|
|
{
|
|
TextComponentString textcomponentstring = new TextComponentString(ScorePlayerTeam.formatPlayerName(this.getTeam(), this.getName()));
|
|
textcomponentstring.getStyle().setHoverEvent(this.getHoverEvent());
|
|
textcomponentstring.getStyle().setInsertion(this.getCachedUniqueIdString());
|
|
return textcomponentstring;
|
|
}
|
|
|
|
/**
|
|
* Sets the custom name tag for this entity
|
|
*/
|
|
public void setCustomNameTag(String name)
|
|
{
|
|
this.dataManager.set(CUSTOM_NAME, name);
|
|
}
|
|
|
|
public String getCustomNameTag()
|
|
{
|
|
return (String)this.dataManager.get(CUSTOM_NAME);
|
|
}
|
|
|
|
/**
|
|
* Returns true if this thing is named
|
|
*/
|
|
public boolean hasCustomName()
|
|
{
|
|
return !((String)this.dataManager.get(CUSTOM_NAME)).isEmpty();
|
|
}
|
|
|
|
public void setAlwaysRenderNameTag(boolean alwaysRenderNameTag)
|
|
{
|
|
this.dataManager.set(CUSTOM_NAME_VISIBLE, Boolean.valueOf(alwaysRenderNameTag));
|
|
}
|
|
|
|
public boolean getAlwaysRenderNameTag()
|
|
{
|
|
return ((Boolean)this.dataManager.get(CUSTOM_NAME_VISIBLE)).booleanValue();
|
|
}
|
|
|
|
/**
|
|
* Sets the position of the entity and updates the 'last' variables
|
|
*/
|
|
public void setPositionAndUpdate(double x, double y, double z)
|
|
{
|
|
this.isPositionDirty = true;
|
|
this.setLocationAndAngles(x, y, z, this.rotationYaw, this.rotationPitch);
|
|
this.world.updateEntityWithOptionalForce(this, false);
|
|
}
|
|
|
|
public void notifyDataManagerChange(DataParameter<?> key)
|
|
{
|
|
}
|
|
|
|
@SideOnly(Side.CLIENT)
|
|
public boolean getAlwaysRenderNameTagForRender()
|
|
{
|
|
return this.getAlwaysRenderNameTag();
|
|
}
|
|
|
|
/**
|
|
* Gets the horizontal facing direction of this Entity.
|
|
*/
|
|
public EnumFacing getHorizontalFacing()
|
|
{
|
|
return EnumFacing.getHorizontal(MathHelper.floor((double)(this.rotationYaw * 4.0F / 360.0F) + 0.5D) & 3);
|
|
}
|
|
|
|
/**
|
|
* Gets the horizontal facing direction of this Entity, adjusted to take specially-treated entity types into
|
|
* account.
|
|
*/
|
|
public EnumFacing getAdjustedHorizontalFacing()
|
|
{
|
|
return this.getHorizontalFacing();
|
|
}
|
|
|
|
protected HoverEvent getHoverEvent()
|
|
{
|
|
NBTTagCompound nbttagcompound = new NBTTagCompound();
|
|
ResourceLocation resourcelocation = EntityList.getKey(this);
|
|
nbttagcompound.setString("id", this.getCachedUniqueIdString());
|
|
|
|
if (resourcelocation != null)
|
|
{
|
|
nbttagcompound.setString("type", resourcelocation.toString());
|
|
}
|
|
|
|
nbttagcompound.setString("name", this.getName());
|
|
return new HoverEvent(HoverEvent.Action.SHOW_ENTITY, new TextComponentString(nbttagcompound.toString()));
|
|
}
|
|
|
|
public boolean isSpectatedByPlayer(EntityPlayerMP player)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
public AxisAlignedBB getEntityBoundingBox()
|
|
{
|
|
return this.boundingBox;
|
|
}
|
|
|
|
/**
|
|
* Gets the bounding box of this Entity, adjusted to take auxiliary entities into account (e.g. the tile contained
|
|
* by a minecart, such as a command block).
|
|
*/
|
|
@SideOnly(Side.CLIENT)
|
|
public AxisAlignedBB getRenderBoundingBox()
|
|
{
|
|
return this.getEntityBoundingBox();
|
|
}
|
|
|
|
public void setEntityBoundingBox(AxisAlignedBB bb)
|
|
{
|
|
this.boundingBox = bb;
|
|
}
|
|
|
|
public float getEyeHeight()
|
|
{
|
|
return this.height * 0.85F;
|
|
}
|
|
|
|
public boolean isOutsideBorder()
|
|
{
|
|
return this.isOutsideBorder;
|
|
}
|
|
|
|
public void setOutsideBorder(boolean outsideBorder)
|
|
{
|
|
this.isOutsideBorder = outsideBorder;
|
|
}
|
|
|
|
public boolean replaceItemInInventory(int inventorySlot, ItemStack itemStackIn)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Send a chat message to the CommandSender
|
|
*/
|
|
public void sendMessage(ITextComponent component)
|
|
{
|
|
}
|
|
|
|
/**
|
|
* Returns {@code true} if the CommandSender is allowed to execute the command, {@code false} if not
|
|
*/
|
|
public boolean canUseCommand(int permLevel, String commandName)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Get the position in the world. <b>{@code null} is not allowed!</b> If you are not an entity in the world, return
|
|
* the coordinates 0, 0, 0
|
|
*/
|
|
public BlockPos getPosition()
|
|
{
|
|
return new BlockPos(this.posX, this.posY + 0.5D, this.posZ);
|
|
}
|
|
|
|
/**
|
|
* Get the position vector. <b>{@code null} is not allowed!</b> If you are not an entity in the world, return 0.0D,
|
|
* 0.0D, 0.0D
|
|
*/
|
|
public Vec3d getPositionVector()
|
|
{
|
|
return new Vec3d(this.posX, this.posY, this.posZ);
|
|
}
|
|
|
|
/**
|
|
* Get the world, if available. <b>{@code null} is not allowed!</b> If you are not an entity in the world, return
|
|
* the overworld
|
|
*/
|
|
public World getEntityWorld()
|
|
{
|
|
return this.world;
|
|
}
|
|
|
|
/**
|
|
* Returns the entity associated with the command sender. MAY BE NULL!
|
|
*/
|
|
public Entity getCommandSenderEntity()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Returns true if the command sender should be sent feedback about executed commands
|
|
*/
|
|
public boolean sendCommandFeedback()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
public void setCommandStat(CommandResultStats.Type type, int amount)
|
|
{
|
|
if (this.world != null && !this.world.isRemote)
|
|
{
|
|
this.cmdResultStats.setCommandStatForSender(this.world.getMinecraftServer(), this, type, amount);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the Minecraft server instance
|
|
*/
|
|
@Nullable
|
|
public MinecraftServer getServer()
|
|
{
|
|
return this.world.getMinecraftServer();
|
|
}
|
|
|
|
public CommandResultStats getCommandStats()
|
|
{
|
|
return this.cmdResultStats;
|
|
}
|
|
|
|
/**
|
|
* Set the CommandResultStats from the entity
|
|
*/
|
|
public void setCommandStats(Entity entityIn)
|
|
{
|
|
this.cmdResultStats.addAllStats(entityIn.getCommandStats());
|
|
}
|
|
|
|
/**
|
|
* Applies the given player interaction to this Entity.
|
|
*/
|
|
public EnumActionResult applyPlayerInteraction(EntityPlayer player, Vec3d vec, EnumHand hand)
|
|
{
|
|
return EnumActionResult.PASS;
|
|
}
|
|
|
|
public boolean isImmuneToExplosions()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
protected void applyEnchantments(EntityLivingBase entityLivingBaseIn, Entity entityIn)
|
|
{
|
|
if (entityIn instanceof EntityLivingBase)
|
|
{
|
|
EnchantmentHelper.applyThornEnchantments((EntityLivingBase)entityIn, entityLivingBaseIn);
|
|
}
|
|
|
|
EnchantmentHelper.applyArthropodEnchantments(entityLivingBaseIn, entityIn);
|
|
}
|
|
|
|
/* ================================== Forge Start =====================================*/
|
|
/**
|
|
* Returns a NBTTagCompound that can be used to store custom data for this entity.
|
|
* It will be written, and read from disc, so it persists over world saves.
|
|
* @return A NBTTagCompound
|
|
*/
|
|
public NBTTagCompound getEntityData()
|
|
{
|
|
if (customEntityData == null)
|
|
{
|
|
customEntityData = new NBTTagCompound();
|
|
}
|
|
return customEntityData;
|
|
}
|
|
|
|
/**
|
|
* Used in model rendering to determine if the entity riding this entity should be in the 'sitting' position.
|
|
* @return false to prevent an entity that is mounted to this entity from displaying the 'sitting' animation.
|
|
*/
|
|
public boolean shouldRiderSit()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Called when a user uses the creative pick block button on this entity.
|
|
*
|
|
* @param target The full target the player is looking at
|
|
* @return A ItemStack to add to the player's inventory, empty ItemStack if nothing should be added.
|
|
*/
|
|
public ItemStack getPickedResult(RayTraceResult target)
|
|
{
|
|
if (this instanceof net.minecraft.entity.item.EntityPainting)
|
|
{
|
|
return new ItemStack(net.minecraft.init.Items.PAINTING);
|
|
}
|
|
else if (this instanceof EntityLeashKnot)
|
|
{
|
|
return new ItemStack(net.minecraft.init.Items.LEAD);
|
|
}
|
|
else if (this instanceof net.minecraft.entity.item.EntityItemFrame)
|
|
{
|
|
ItemStack held = ((net.minecraft.entity.item.EntityItemFrame)this).getDisplayedItem();
|
|
if (held.isEmpty())
|
|
{
|
|
return new ItemStack(net.minecraft.init.Items.ITEM_FRAME);
|
|
}
|
|
else
|
|
{
|
|
return held.copy();
|
|
}
|
|
}
|
|
else if (this instanceof net.minecraft.entity.item.EntityMinecart)
|
|
{
|
|
return ((net.minecraft.entity.item.EntityMinecart)this).getCartItem();
|
|
}
|
|
else if (this instanceof net.minecraft.entity.item.EntityBoat)
|
|
{
|
|
return new ItemStack(((EntityBoat)this).getItemBoat());
|
|
}
|
|
else if (this instanceof net.minecraft.entity.item.EntityArmorStand)
|
|
{
|
|
return new ItemStack(net.minecraft.init.Items.ARMOR_STAND);
|
|
}
|
|
else if (this instanceof net.minecraft.entity.item.EntityEnderCrystal)
|
|
{
|
|
return new ItemStack(net.minecraft.init.Items.END_CRYSTAL);
|
|
}
|
|
else
|
|
{
|
|
ResourceLocation name = EntityList.getKey(this);
|
|
if (name != null && EntityList.ENTITY_EGGS.containsKey(name))
|
|
{
|
|
ItemStack stack = new ItemStack(net.minecraft.init.Items.SPAWN_EGG);
|
|
net.minecraft.item.ItemMonsterPlacer.applyEntityIdToItemStack(stack, name);
|
|
return stack;
|
|
}
|
|
}
|
|
return ItemStack.EMPTY;
|
|
}
|
|
|
|
public UUID getPersistentID()
|
|
{
|
|
return entityUniqueID;
|
|
}
|
|
|
|
/**
|
|
* Reset the entity ID to a new value. Not to be used from Mod code
|
|
*/
|
|
@Deprecated // TODO: remove (1.13?)
|
|
public final void resetEntityId()
|
|
{
|
|
this.entityId = nextEntityID++;
|
|
}
|
|
|
|
public boolean shouldRenderInPass(int pass)
|
|
{
|
|
return pass == 0;
|
|
}
|
|
|
|
/**
|
|
* Returns true if the entity is of the @link{EnumCreatureType} provided
|
|
* @param type The EnumCreatureType type this entity is evaluating
|
|
* @param forSpawnCount If this is being invoked to check spawn count caps.
|
|
* @return If the creature is of the type provided
|
|
*/
|
|
public boolean isCreatureType(EnumCreatureType type, boolean forSpawnCount)
|
|
{
|
|
if (forSpawnCount && (this instanceof EntityLiving) && ((EntityLiving)this).isNoDespawnRequired()) return false;
|
|
return type.getCreatureClass().isAssignableFrom(this.getClass());
|
|
}
|
|
|
|
/**
|
|
* If a rider of this entity can interact with this entity. Should return true on the
|
|
* ridden entity if so.
|
|
*
|
|
* @return if the entity can be interacted with from a rider
|
|
*/
|
|
public boolean canRiderInteract()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* If the rider should be dismounted from the entity when the entity goes under water
|
|
*
|
|
* @param rider The entity that is riding
|
|
* @return if the entity should be dismounted when under water
|
|
*/
|
|
public boolean shouldDismountInWater(Entity rider)
|
|
{
|
|
return this instanceof EntityLivingBase;
|
|
}
|
|
|
|
@Override
|
|
public boolean hasCapability(net.minecraftforge.common.capabilities.Capability<?> capability, @Nullable net.minecraft.util.EnumFacing facing)
|
|
{
|
|
return capabilities != null && capabilities.hasCapability(capability, facing);
|
|
}
|
|
|
|
@Override
|
|
@Nullable
|
|
public <T> T getCapability(net.minecraftforge.common.capabilities.Capability<T> capability, @Nullable net.minecraft.util.EnumFacing facing)
|
|
{
|
|
return capabilities == null ? null : capabilities.getCapability(capability, facing);
|
|
}
|
|
|
|
public void deserializeNBT(NBTTagCompound nbt)
|
|
{
|
|
this.readFromNBT(nbt);
|
|
}
|
|
|
|
public NBTTagCompound serializeNBT()
|
|
{
|
|
NBTTagCompound ret = new NBTTagCompound();
|
|
ret.setString("id", this.getEntityString());
|
|
return this.writeToNBT(ret);
|
|
}
|
|
|
|
/**
|
|
* Checks if this {@link Entity} can trample a {@link Block}.
|
|
*
|
|
* @param world The world in which the block will be trampled
|
|
* @param block The block being tested
|
|
* @param pos The block pos
|
|
* @param fallDistance The fall distance
|
|
* @return {@code true} if this entity can trample, {@code false} otherwise
|
|
*/
|
|
public boolean canTrample(World world, Block block, BlockPos pos, float fallDistance)
|
|
{
|
|
return world.rand.nextFloat() < fallDistance - 0.5F
|
|
&& this instanceof EntityLivingBase
|
|
&& (this instanceof EntityPlayer || net.minecraftforge.event.ForgeEventFactory.getMobGriefingEvent(world, this))
|
|
&& this.width * this.width * this.height > 0.512F;
|
|
}
|
|
/* ================================== Forge End =====================================*/
|
|
|
|
/**
|
|
* Add the given player to the list of players tracking this entity. For instance, a player may track a boss in
|
|
* order to view its associated boss bar.
|
|
*/
|
|
public void addTrackingPlayer(EntityPlayerMP player)
|
|
{
|
|
}
|
|
|
|
/**
|
|
* Removes the given player from the list of players tracking this entity. See {@link Entity#addTrackingPlayer} for
|
|
* more information on tracking.
|
|
*/
|
|
public void removeTrackingPlayer(EntityPlayerMP player)
|
|
{
|
|
}
|
|
|
|
/**
|
|
* Transforms the entity's current yaw with the given Rotation and returns it. This does not have a side-effect.
|
|
*/
|
|
public float getRotatedYaw(Rotation transformRotation)
|
|
{
|
|
float f = MathHelper.wrapDegrees(this.rotationYaw);
|
|
|
|
switch (transformRotation)
|
|
{
|
|
case CLOCKWISE_180:
|
|
return f + 180.0F;
|
|
case COUNTERCLOCKWISE_90:
|
|
return f + 270.0F;
|
|
case CLOCKWISE_90:
|
|
return f + 90.0F;
|
|
default:
|
|
return f;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Transforms the entity's current yaw with the given Mirror and returns it. This does not have a side-effect.
|
|
*/
|
|
public float getMirroredYaw(Mirror transformMirror)
|
|
{
|
|
float f = MathHelper.wrapDegrees(this.rotationYaw);
|
|
|
|
switch (transformMirror)
|
|
{
|
|
case LEFT_RIGHT:
|
|
return -f;
|
|
case FRONT_BACK:
|
|
return 180.0F - f;
|
|
default:
|
|
return f;
|
|
}
|
|
}
|
|
|
|
public boolean ignoreItemEntityData()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
public boolean setPositionNonDirty()
|
|
{
|
|
boolean flag = this.isPositionDirty;
|
|
this.isPositionDirty = false;
|
|
return flag;
|
|
}
|
|
|
|
/**
|
|
* For vehicles, the first passenger is generally considered the controller and "drives" the vehicle. For example,
|
|
* Pigs, Horses, and Boats are generally "steered" by the controlling passenger.
|
|
*/
|
|
@Nullable
|
|
public Entity getControllingPassenger()
|
|
{
|
|
return null;
|
|
}
|
|
|
|
public List<Entity> getPassengers()
|
|
{
|
|
return (List<Entity>)(this.riddenByEntities.isEmpty() ? Collections.emptyList() : Lists.newArrayList(this.riddenByEntities));
|
|
}
|
|
|
|
public boolean isPassenger(Entity entityIn)
|
|
{
|
|
for (Entity entity : this.getPassengers())
|
|
{
|
|
if (entity.equals(entityIn))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Recursively collects the passengers of this entity. This differs from getPassengers() in that passengers of
|
|
* passengers are recursively collected.
|
|
*/
|
|
public Collection<Entity> getRecursivePassengers()
|
|
{
|
|
Set<Entity> set = Sets.<Entity>newHashSet();
|
|
this.getRecursivePassengersByType(Entity.class, set);
|
|
return set;
|
|
}
|
|
|
|
/**
|
|
* Recursively collects the passengers of this entity with type denoted by the given class.
|
|
*/
|
|
public <T extends Entity> Collection<T> getRecursivePassengersByType(Class<T> entityClass)
|
|
{
|
|
Set<T> set = Sets.<T>newHashSet();
|
|
this.getRecursivePassengersByType(entityClass, set);
|
|
return set;
|
|
}
|
|
|
|
/**
|
|
* Recursively collects the passengers of this entity with the type denoted by the given class into the given Set.
|
|
*/
|
|
private <T extends Entity> void getRecursivePassengersByType(Class<T> entityClass, Set<T> theSet)
|
|
{
|
|
for (Entity entity : this.getPassengers())
|
|
{
|
|
if (entityClass.isAssignableFrom(entity.getClass()))
|
|
{
|
|
theSet.add((T)entity);
|
|
}
|
|
|
|
entity.getRecursivePassengersByType(entityClass, theSet);
|
|
}
|
|
}
|
|
|
|
public Entity getLowestRidingEntity()
|
|
{
|
|
Entity entity;
|
|
|
|
for (entity = this; entity.isRiding(); entity = entity.getRidingEntity())
|
|
{
|
|
;
|
|
}
|
|
|
|
return entity;
|
|
}
|
|
|
|
public boolean isRidingSameEntity(Entity entityIn)
|
|
{
|
|
return this.getLowestRidingEntity() == entityIn.getLowestRidingEntity();
|
|
}
|
|
|
|
public boolean isRidingOrBeingRiddenBy(Entity entityIn)
|
|
{
|
|
for (Entity entity : this.getPassengers())
|
|
{
|
|
if (entity.equals(entityIn))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (entity.isRidingOrBeingRiddenBy(entityIn))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public boolean canPassengerSteer()
|
|
{
|
|
Entity entity = this.getControllingPassenger();
|
|
|
|
if (entity instanceof EntityPlayer)
|
|
{
|
|
return ((EntityPlayer)entity).isUser();
|
|
}
|
|
else
|
|
{
|
|
return !this.world.isRemote;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get entity this is riding
|
|
*/
|
|
@Nullable
|
|
public Entity getRidingEntity()
|
|
{
|
|
return this.ridingEntity;
|
|
}
|
|
|
|
public EnumPushReaction getPushReaction()
|
|
{
|
|
return EnumPushReaction.NORMAL;
|
|
}
|
|
|
|
public SoundCategory getSoundCategory()
|
|
{
|
|
return SoundCategory.NEUTRAL;
|
|
}
|
|
|
|
protected int getFireImmuneTicks()
|
|
{
|
|
return 1;
|
|
}
|
|
} |