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 getLineOfSight(HashSet transparent, int maxDistance, int maxLength) { if (maxDistance > 120) { maxDistance = 120; } ArrayList blocks = new ArrayList(); Iterator 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 getLineOfSight(Set transparent, int maxDistance, int maxLength) { if (maxDistance > 120) { maxDistance = 120; } ArrayList blocks = new ArrayList(); Iterator 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 getLineOfSight(HashSet transparent, int maxDistance) { return getLineOfSight(transparent, maxDistance, 0); } public List getLineOfSight(Set transparent, int maxDistance) { return getLineOfSight(transparent, maxDistance, 0); } public Block getTargetBlock(HashSet transparent, int maxDistance) { List blocks = getLineOfSight(transparent, maxDistance, 1); return blocks.get(0); } public Block getTargetBlock(Set transparent, int maxDistance) { List blocks = getLineOfSight(transparent, maxDistance, 1); return blocks.get(0); } public List getLastTwoTargetBlocks(HashSet transparent, int maxDistance) { return getLineOfSight(transparent, maxDistance, 2); } public List getLastTwoTargetBlocks(Set 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 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 getActivePotionEffects() { List effects = new ArrayList(); 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 launchProjectile(Class projectile) { return launchProjectile(projectile, null); } @SuppressWarnings("unchecked") public T launchProjectile(Class 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; } }