560 lines
19 KiB
Java
560 lines
19 KiB
Java
package org.bukkit.craftbukkit.entity;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.Collection;
|
|
import java.util.HashSet;
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
import java.util.Set;
|
|
|
|
import net.minecraft.server.DamageSource;
|
|
import net.minecraft.server.EntityArmorStand;
|
|
import net.minecraft.server.EntityArrow;
|
|
import net.minecraft.server.EntityDragonFireball;
|
|
import net.minecraft.server.EntityEgg;
|
|
import net.minecraft.server.EntityEnderPearl;
|
|
import net.minecraft.server.EntityFishingHook;
|
|
import net.minecraft.server.EntityHuman;
|
|
import net.minecraft.server.EntityFireball;
|
|
import net.minecraft.server.EntityInsentient;
|
|
import net.minecraft.server.EntityLargeFireball;
|
|
import net.minecraft.server.EntityLiving;
|
|
import net.minecraft.server.EntityPlayer;
|
|
import net.minecraft.server.EntityPotion;
|
|
import net.minecraft.server.EntityProjectile;
|
|
import net.minecraft.server.EntitySmallFireball;
|
|
import net.minecraft.server.EntitySnowball;
|
|
import net.minecraft.server.EntityThrownExpBottle;
|
|
import net.minecraft.server.EntityTippedArrow;
|
|
import net.minecraft.server.EntitySpectralArrow;
|
|
import net.minecraft.server.EntityWither;
|
|
import net.minecraft.server.EntityWitherSkull;
|
|
import net.minecraft.server.GenericAttributes;
|
|
import net.minecraft.server.MobEffect;
|
|
import net.minecraft.server.MobEffectList;
|
|
|
|
import org.apache.commons.lang.Validate;
|
|
import org.bukkit.Location;
|
|
import org.bukkit.Material;
|
|
import org.bukkit.attribute.Attribute;
|
|
import org.bukkit.attribute.AttributeInstance;
|
|
import org.bukkit.block.Block;
|
|
import org.bukkit.craftbukkit.CraftServer;
|
|
import org.bukkit.craftbukkit.CraftWorld;
|
|
import org.bukkit.craftbukkit.inventory.CraftEntityEquipment;
|
|
import org.bukkit.craftbukkit.inventory.CraftItemStack;
|
|
import org.bukkit.craftbukkit.potion.CraftPotionUtil;
|
|
import org.bukkit.entity.Arrow;
|
|
import org.bukkit.entity.DragonFireball;
|
|
import org.bukkit.entity.Egg;
|
|
import org.bukkit.entity.EnderPearl;
|
|
import org.bukkit.entity.Entity;
|
|
import org.bukkit.entity.EntityType;
|
|
import org.bukkit.entity.Fireball;
|
|
import org.bukkit.entity.Fish;
|
|
import org.bukkit.entity.HumanEntity;
|
|
import org.bukkit.entity.LingeringPotion;
|
|
import org.bukkit.entity.LivingEntity;
|
|
import org.bukkit.entity.Player;
|
|
import org.bukkit.entity.Projectile;
|
|
import org.bukkit.entity.SmallFireball;
|
|
import org.bukkit.entity.Snowball;
|
|
import org.bukkit.entity.SpectralArrow;
|
|
import org.bukkit.entity.ThrownExpBottle;
|
|
import org.bukkit.entity.ThrownPotion;
|
|
import org.bukkit.entity.TippedArrow;
|
|
import org.bukkit.entity.WitherSkull;
|
|
import org.bukkit.event.player.PlayerTeleportEvent;
|
|
import org.bukkit.inventory.EntityEquipment;
|
|
import org.bukkit.inventory.ItemStack;
|
|
import org.bukkit.potion.PotionData;
|
|
import org.bukkit.potion.PotionEffect;
|
|
import org.bukkit.potion.PotionEffectType;
|
|
import org.bukkit.potion.PotionType;
|
|
import org.bukkit.util.BlockIterator;
|
|
import org.bukkit.util.NumberConversions;
|
|
import org.bukkit.util.Vector;
|
|
|
|
public class CraftLivingEntity extends CraftEntity implements LivingEntity {
|
|
private CraftEntityEquipment equipment;
|
|
|
|
public CraftLivingEntity(final CraftServer server, final EntityLiving entity) {
|
|
super(server, entity);
|
|
|
|
if (entity instanceof EntityInsentient || entity instanceof EntityArmorStand) {
|
|
equipment = new CraftEntityEquipment(this);
|
|
}
|
|
}
|
|
|
|
public double getHealth() {
|
|
return Math.min(Math.max(0, getHandle().getHealth()), getMaxHealth());
|
|
}
|
|
|
|
public void setHealth(double health) {
|
|
if ((health < 0) || (health > getMaxHealth())) {
|
|
throw new IllegalArgumentException("Health must be between 0 and " + getMaxHealth());
|
|
}
|
|
|
|
if (health == 0) {
|
|
getHandle().die(DamageSource.GENERIC);
|
|
}
|
|
|
|
getHandle().setHealth((float) health);
|
|
}
|
|
|
|
public double getMaxHealth() {
|
|
return getHandle().getMaxHealth();
|
|
}
|
|
|
|
public void setMaxHealth(double amount) {
|
|
Validate.isTrue(amount > 0, "Max health must be greater than 0");
|
|
|
|
getHandle().getAttributeInstance(GenericAttributes.maxHealth).setValue(amount);
|
|
|
|
if (getHealth() > amount) {
|
|
setHealth(amount);
|
|
}
|
|
}
|
|
|
|
public void resetMaxHealth() {
|
|
setMaxHealth(getHandle().getAttributeInstance(GenericAttributes.maxHealth).getAttribute().getDefault());
|
|
}
|
|
|
|
public double getEyeHeight() {
|
|
return getHandle().getHeadHeight();
|
|
}
|
|
|
|
public double getEyeHeight(boolean ignoreSneaking) {
|
|
return getEyeHeight();
|
|
}
|
|
|
|
private List<Block> getLineOfSight(HashSet<Byte> transparent, int maxDistance, int maxLength) {
|
|
if (maxDistance > 120) {
|
|
maxDistance = 120;
|
|
}
|
|
ArrayList<Block> blocks = new ArrayList<Block>();
|
|
Iterator<Block> itr = new BlockIterator(this, maxDistance);
|
|
while (itr.hasNext()) {
|
|
Block block = itr.next();
|
|
blocks.add(block);
|
|
if (maxLength != 0 && blocks.size() > maxLength) {
|
|
blocks.remove(0);
|
|
}
|
|
int id = block.getTypeId();
|
|
if (transparent == null) {
|
|
if (id != 0) {
|
|
break;
|
|
}
|
|
} else {
|
|
if (!transparent.contains((byte) id)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return blocks;
|
|
}
|
|
|
|
private List<Block> getLineOfSight(Set<Material> transparent, int maxDistance, int maxLength) {
|
|
if (maxDistance > 120) {
|
|
maxDistance = 120;
|
|
}
|
|
ArrayList<Block> blocks = new ArrayList<Block>();
|
|
Iterator<Block> itr = new BlockIterator(this, maxDistance);
|
|
while (itr.hasNext()) {
|
|
Block block = itr.next();
|
|
blocks.add(block);
|
|
if (maxLength != 0 && blocks.size() > maxLength) {
|
|
blocks.remove(0);
|
|
}
|
|
Material material = block.getType();
|
|
if (transparent == null) {
|
|
if (!material.equals(Material.AIR)) {
|
|
break;
|
|
}
|
|
} else {
|
|
if (!transparent.contains(material)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return blocks;
|
|
}
|
|
|
|
public List<Block> getLineOfSight(HashSet<Byte> transparent, int maxDistance) {
|
|
return getLineOfSight(transparent, maxDistance, 0);
|
|
}
|
|
|
|
public List<Block> getLineOfSight(Set<Material> transparent, int maxDistance) {
|
|
return getLineOfSight(transparent, maxDistance, 0);
|
|
}
|
|
|
|
public Block getTargetBlock(HashSet<Byte> transparent, int maxDistance) {
|
|
List<Block> blocks = getLineOfSight(transparent, maxDistance, 1);
|
|
return blocks.get(0);
|
|
}
|
|
|
|
public Block getTargetBlock(Set<Material> transparent, int maxDistance) {
|
|
List<Block> blocks = getLineOfSight(transparent, maxDistance, 1);
|
|
return blocks.get(0);
|
|
}
|
|
|
|
public List<Block> getLastTwoTargetBlocks(HashSet<Byte> transparent, int maxDistance) {
|
|
return getLineOfSight(transparent, maxDistance, 2);
|
|
}
|
|
|
|
public List<Block> getLastTwoTargetBlocks(Set<Material> transparent, int maxDistance) {
|
|
return getLineOfSight(transparent, maxDistance, 2);
|
|
}
|
|
|
|
public int getRemainingAir() {
|
|
return getHandle().getAirTicks();
|
|
}
|
|
|
|
public void setRemainingAir(int ticks) {
|
|
getHandle().setAirTicks(ticks);
|
|
}
|
|
|
|
public int getMaximumAir() {
|
|
return getHandle().maxAirTicks;
|
|
}
|
|
|
|
public void setMaximumAir(int ticks) {
|
|
getHandle().maxAirTicks = ticks;
|
|
}
|
|
|
|
public void damage(double amount) {
|
|
damage(amount, null);
|
|
}
|
|
|
|
public void damage(double amount, org.bukkit.entity.Entity source) {
|
|
DamageSource reason = DamageSource.GENERIC;
|
|
|
|
if (source instanceof HumanEntity) {
|
|
reason = DamageSource.playerAttack(((CraftHumanEntity) source).getHandle());
|
|
} else if (source instanceof LivingEntity) {
|
|
reason = DamageSource.mobAttack(((CraftLivingEntity) source).getHandle());
|
|
}
|
|
|
|
entity.damageEntity(reason, (float) amount);
|
|
}
|
|
|
|
public Location getEyeLocation() {
|
|
Location loc = getLocation();
|
|
loc.setY(loc.getY() + getEyeHeight());
|
|
return loc;
|
|
}
|
|
|
|
public int getMaximumNoDamageTicks() {
|
|
return getHandle().maxNoDamageTicks;
|
|
}
|
|
|
|
public void setMaximumNoDamageTicks(int ticks) {
|
|
getHandle().maxNoDamageTicks = ticks;
|
|
}
|
|
|
|
public double getLastDamage() {
|
|
return getHandle().lastDamage;
|
|
}
|
|
|
|
public void setLastDamage(double damage) {
|
|
getHandle().lastDamage = (float) damage;
|
|
}
|
|
|
|
public int getNoDamageTicks() {
|
|
return getHandle().noDamageTicks;
|
|
}
|
|
|
|
public void setNoDamageTicks(int ticks) {
|
|
getHandle().noDamageTicks = ticks;
|
|
}
|
|
|
|
@Override
|
|
public EntityLiving getHandle() {
|
|
return (EntityLiving) entity;
|
|
}
|
|
|
|
public void setHandle(final EntityLiving entity) {
|
|
super.setHandle(entity);
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return "CraftLivingEntity{" + "id=" + getEntityId() + '}';
|
|
}
|
|
|
|
public Player getKiller() {
|
|
return getHandle().killer == null ? null : (Player) getHandle().killer.getBukkitEntity();
|
|
}
|
|
|
|
public boolean addPotionEffect(PotionEffect effect) {
|
|
return addPotionEffect(effect, false);
|
|
}
|
|
|
|
public boolean addPotionEffect(PotionEffect effect, boolean force) {
|
|
if (hasPotionEffect(effect.getType())) {
|
|
if (!force) {
|
|
return false;
|
|
}
|
|
removePotionEffect(effect.getType());
|
|
}
|
|
getHandle().addEffect(new MobEffect(MobEffectList.fromId(effect.getType().getId()), effect.getDuration(), effect.getAmplifier(), effect.isAmbient(), effect.hasParticles()));
|
|
return true;
|
|
}
|
|
|
|
public boolean addPotionEffects(Collection<PotionEffect> effects) {
|
|
boolean success = true;
|
|
for (PotionEffect effect : effects) {
|
|
success &= addPotionEffect(effect);
|
|
}
|
|
return success;
|
|
}
|
|
|
|
public boolean hasPotionEffect(PotionEffectType type) {
|
|
return getHandle().hasEffect(MobEffectList.fromId(type.getId()));
|
|
}
|
|
|
|
@Override
|
|
public PotionEffect getPotionEffect(PotionEffectType type) {
|
|
MobEffect handle = getHandle().getEffect(MobEffectList.fromId(type.getId()));
|
|
return (handle == null) ? null : new PotionEffect(PotionEffectType.getById(MobEffectList.getId(handle.getMobEffect())), handle.getDuration(), handle.getAmplifier(), handle.isAmbient(), handle.isShowParticles());
|
|
}
|
|
|
|
public void removePotionEffect(PotionEffectType type) {
|
|
getHandle().removeEffect(MobEffectList.fromId(type.getId()));
|
|
}
|
|
|
|
public Collection<PotionEffect> getActivePotionEffects() {
|
|
List<PotionEffect> effects = new ArrayList<PotionEffect>();
|
|
for (MobEffect handle : getHandle().effects.values()) {
|
|
effects.add(new PotionEffect(PotionEffectType.getById(MobEffectList.getId(handle.getMobEffect())), handle.getDuration(), handle.getAmplifier(), handle.isAmbient(), handle.isShowParticles()));
|
|
}
|
|
return effects;
|
|
}
|
|
|
|
public <T extends Projectile> T launchProjectile(Class<? extends T> projectile) {
|
|
return launchProjectile(projectile, null);
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
public <T extends Projectile> T launchProjectile(Class<? extends T> projectile, Vector velocity) {
|
|
net.minecraft.server.World world = ((CraftWorld) getWorld()).getHandle();
|
|
net.minecraft.server.Entity launch = null;
|
|
|
|
if (Snowball.class.isAssignableFrom(projectile)) {
|
|
launch = new EntitySnowball(world, getHandle());
|
|
((EntityProjectile) launch).a(getHandle(), getHandle().pitch, getHandle().yaw, 0.0F, 1.5F, 1.0F); // ItemSnowball
|
|
} else if (Egg.class.isAssignableFrom(projectile)) {
|
|
launch = new EntityEgg(world, getHandle());
|
|
((EntityProjectile) launch).a(getHandle(), getHandle().pitch, getHandle().yaw, 0.0F, 1.5F, 1.0F); // ItemEgg
|
|
} else if (EnderPearl.class.isAssignableFrom(projectile)) {
|
|
launch = new EntityEnderPearl(world, getHandle());
|
|
((EntityProjectile) launch).a(getHandle(), getHandle().pitch, getHandle().yaw, 0.0F, 1.5F, 1.0F); // ItemEnderPearl
|
|
} else if (Arrow.class.isAssignableFrom(projectile)) {
|
|
if (TippedArrow.class.isAssignableFrom(projectile)) {
|
|
launch = new EntityTippedArrow(world, getHandle());
|
|
((EntityTippedArrow) launch).setType(CraftPotionUtil.fromBukkit(new PotionData(PotionType.WATER, false, false)));
|
|
} else if (SpectralArrow.class.isAssignableFrom(projectile)) {
|
|
launch = new EntitySpectralArrow(world, getHandle());
|
|
} else {
|
|
launch = new EntityTippedArrow(world, getHandle());
|
|
}
|
|
((EntityArrow) launch).a(getHandle(), getHandle().pitch, getHandle().yaw, 0.0F, 3.0F, 1.0F); // ItemBow
|
|
} else if (ThrownPotion.class.isAssignableFrom(projectile)) {
|
|
if (LingeringPotion.class.isAssignableFrom(projectile)) {
|
|
launch = new EntityPotion(world, getHandle(), CraftItemStack.asNMSCopy(new ItemStack(org.bukkit.Material.LINGERING_POTION, 1)));
|
|
} else {
|
|
launch = new EntityPotion(world, getHandle(), CraftItemStack.asNMSCopy(new ItemStack(org.bukkit.Material.SPLASH_POTION, 1)));
|
|
}
|
|
((EntityProjectile) launch).a(getHandle(), getHandle().pitch, getHandle().yaw, -20.0F, 0.5F, 1.0F); // ItemSplashPotion
|
|
} else if (ThrownExpBottle.class.isAssignableFrom(projectile)) {
|
|
launch = new EntityThrownExpBottle(world, getHandle());
|
|
((EntityProjectile) launch).a(getHandle(), getHandle().pitch, getHandle().yaw, -20.0F, 0.7F, 1.0F); // ItemExpBottle
|
|
} else if (Fish.class.isAssignableFrom(projectile) && getHandle() instanceof EntityHuman) {
|
|
launch = new EntityFishingHook(world, (EntityHuman) getHandle());
|
|
} else if (Fireball.class.isAssignableFrom(projectile)) {
|
|
Location location = getEyeLocation();
|
|
Vector direction = location.getDirection().multiply(10);
|
|
|
|
if (SmallFireball.class.isAssignableFrom(projectile)) {
|
|
launch = new EntitySmallFireball(world, getHandle(), direction.getX(), direction.getY(), direction.getZ());
|
|
} else if (WitherSkull.class.isAssignableFrom(projectile)) {
|
|
launch = new EntityWitherSkull(world, getHandle(), direction.getX(), direction.getY(), direction.getZ());
|
|
} else if (DragonFireball.class.isAssignableFrom(projectile)) {
|
|
launch = new EntityDragonFireball(world, getHandle(), direction.getX(), direction.getY(), direction.getZ());
|
|
} else {
|
|
launch = new EntityLargeFireball(world, getHandle(), direction.getX(), direction.getY(), direction.getZ());
|
|
}
|
|
|
|
((EntityFireball) launch).projectileSource = this;
|
|
launch.setPositionRotation(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch());
|
|
}
|
|
|
|
Validate.notNull(launch, "Projectile not supported");
|
|
|
|
if (velocity != null) {
|
|
((T) launch.getBukkitEntity()).setVelocity(velocity);
|
|
}
|
|
|
|
world.addEntity(launch);
|
|
return (T) launch.getBukkitEntity();
|
|
}
|
|
|
|
public EntityType getType() {
|
|
return EntityType.UNKNOWN;
|
|
}
|
|
|
|
public boolean hasLineOfSight(Entity other) {
|
|
return getHandle().hasLineOfSight(((CraftEntity) other).getHandle());
|
|
}
|
|
|
|
public boolean getRemoveWhenFarAway() {
|
|
return getHandle() instanceof EntityInsentient && !((EntityInsentient) getHandle()).persistent;
|
|
}
|
|
|
|
public void setRemoveWhenFarAway(boolean remove) {
|
|
if (getHandle() instanceof EntityInsentient) {
|
|
((EntityInsentient) getHandle()).persistent = !remove;
|
|
}
|
|
}
|
|
|
|
public EntityEquipment getEquipment() {
|
|
return equipment;
|
|
}
|
|
|
|
public void setCanPickupItems(boolean pickup) {
|
|
if (getHandle() instanceof EntityInsentient) {
|
|
((EntityInsentient) getHandle()).canPickUpLoot = pickup;
|
|
}
|
|
}
|
|
|
|
public boolean getCanPickupItems() {
|
|
return getHandle() instanceof EntityInsentient && ((EntityInsentient) getHandle()).canPickUpLoot;
|
|
}
|
|
|
|
@Override
|
|
public boolean teleport(Location location, PlayerTeleportEvent.TeleportCause cause) {
|
|
if (getHealth() == 0) {
|
|
return false;
|
|
}
|
|
|
|
return super.teleport(location, cause);
|
|
}
|
|
|
|
public boolean isLeashed() {
|
|
if (!(getHandle() instanceof EntityInsentient)) {
|
|
return false;
|
|
}
|
|
return ((EntityInsentient) getHandle()).getLeashHolder() != null;
|
|
}
|
|
|
|
public Entity getLeashHolder() throws IllegalStateException {
|
|
if (!isLeashed()) {
|
|
throw new IllegalStateException("Entity not leashed");
|
|
}
|
|
return ((EntityInsentient) getHandle()).getLeashHolder().getBukkitEntity();
|
|
}
|
|
|
|
private boolean unleash() {
|
|
if (!isLeashed()) {
|
|
return false;
|
|
}
|
|
((EntityInsentient) getHandle()).unleash(true, false);
|
|
return true;
|
|
}
|
|
|
|
public boolean setLeashHolder(Entity holder) {
|
|
if ((getHandle() instanceof EntityWither) || !(getHandle() instanceof EntityInsentient)) {
|
|
return false;
|
|
}
|
|
|
|
if (holder == null) {
|
|
return unleash();
|
|
}
|
|
|
|
if (holder.isDead()) {
|
|
return false;
|
|
}
|
|
|
|
unleash();
|
|
((EntityInsentient) getHandle()).setLeashHolder(((CraftEntity) holder).getHandle(), true);
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public boolean isGliding() {
|
|
return getHandle().getFlag(7);
|
|
}
|
|
|
|
@Override
|
|
public void setGliding(boolean gliding) {
|
|
getHandle().setFlag(7, gliding);
|
|
}
|
|
|
|
@Deprecated
|
|
public int _INVALID_getLastDamage() {
|
|
return NumberConversions.ceil(getLastDamage());
|
|
}
|
|
|
|
@Deprecated
|
|
public void _INVALID_setLastDamage(int damage) {
|
|
setLastDamage(damage);
|
|
}
|
|
|
|
@Deprecated
|
|
public void _INVALID_damage(int amount) {
|
|
damage(amount);
|
|
}
|
|
|
|
@Deprecated
|
|
public void _INVALID_damage(int amount, Entity source) {
|
|
damage(amount, source);
|
|
}
|
|
|
|
@Deprecated
|
|
public int _INVALID_getHealth() {
|
|
return NumberConversions.ceil(getHealth());
|
|
}
|
|
|
|
@Deprecated
|
|
public void _INVALID_setHealth(int health) {
|
|
setHealth(health);
|
|
}
|
|
|
|
@Deprecated
|
|
public int _INVALID_getMaxHealth() {
|
|
return NumberConversions.ceil(getMaxHealth());
|
|
}
|
|
|
|
@Deprecated
|
|
public void _INVALID_setMaxHealth(int health) {
|
|
setMaxHealth(health);
|
|
}
|
|
|
|
@Override
|
|
public AttributeInstance getAttribute(Attribute attribute) {
|
|
return getHandle().craftAttributes.getAttribute(attribute);
|
|
}
|
|
|
|
@Override
|
|
public void setAI(boolean ai) {
|
|
if (this.getHandle() instanceof EntityInsentient) {
|
|
((EntityInsentient) this.getHandle()).setAI(!ai);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean hasAI() {
|
|
return (this.getHandle() instanceof EntityInsentient) ? !((EntityInsentient) this.getHandle()).hasAI(): false;
|
|
}
|
|
|
|
@Override
|
|
public void setCollidable(boolean collidable) {
|
|
getHandle().collides = collidable;
|
|
}
|
|
|
|
@Override
|
|
public boolean isCollidable() {
|
|
return getHandle().collides;
|
|
}
|
|
}
|