blob: 0a5f10064d308bd3a28c49192a64e36fbd2cb5be [file] [log] [blame] [raw]
package net.glowstone.entity;
import com.flowpowered.networking.Message;
import net.glowstone.constants.GlowPotionEffect;
import net.glowstone.inventory.EquipmentMonitor;
import net.glowstone.net.message.play.entity.EntityEquipmentMessage;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.entity.*;
import org.bukkit.inventory.EntityEquipment;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.util.BlockIterator;
import org.bukkit.util.Vector;
import java.util.*;
/**
* A GlowLivingEntity is a {@link org.bukkit.entity.Player} or {@link org.bukkit.entity.Monster}.
* @author Graham Edgecombe.
*/
public abstract class GlowLivingEntity extends GlowEntity implements LivingEntity {
/**
* Potion effects on the entity.
*/
private final Map<PotionEffectType, PotionEffect> potionEffects = new HashMap<>();
/**
* The entity's health.
*/
protected double health;
/**
* The entity's maximum health.
*/
protected double maxHealth;
/**
* The magnitude of the last damage the entity took.
*/
private double lastDamage;
/**
* How long the entity has until it runs out of air.
*/
private int airTicks = 300;
/**
* The maximum amount of air the entity can hold.
*/
private int maximumAir = 300;
/**
* The number of ticks remaining in the invincibility period.
*/
private int noDamageTicks = 0;
/**
* The default length of the invincibility period.
*/
private int maxNoDamageTicks = 20;
/**
* A custom overhead name to be shown for non-Players.
*/
private String customName;
/**
* Whether the custom name is shown.
*/
private boolean customNameVisible;
/**
* Whether the entity should be removed if it is too distant from players.
*/
private boolean removeDistance;
/**
* Whether the (non-Player) entity can pick up armor and tools.
*/
private boolean pickupItems;
/**
* Monitor for the equipment of this entity.
*/
private EquipmentMonitor equipmentMonitor = new EquipmentMonitor(this);
/**
* Creates a mob within the specified world.
* @param location The location.
*/
public GlowLivingEntity(Location location) {
super(location);
resetMaxHealth();
health = maxHealth;
}
////////////////////////////////////////////////////////////////////////////
// Internals
@Override
public void pulse() {
super.pulse();
// invulnerability
if (noDamageTicks > 0) {
--noDamageTicks;
}
// breathing
Material mat = getEyeLocation().getBlock().getType();
if (mat == Material.WATER || mat == Material.STATIONARY_WATER) {
--airTicks;
if (airTicks <= -20) {
airTicks = 0;
// todo: indicate that the damage was caused by drowning
damage(1);
}
} else {
airTicks = maximumAir;
}
// potion effects
List<PotionEffect> effects = new ArrayList<>(potionEffects.values());
for (PotionEffect effect : effects) {
// pulse effect
GlowPotionEffect type = (GlowPotionEffect) effect.getType();
type.pulse(this, effect);
if (effect.getDuration() > 0) {
// reduce duration and re-add
addPotionEffect(new PotionEffect(type, effect.getDuration() - 1, effect.getAmplifier(), effect.isAmbient()), true);
} else {
// remove
removePotionEffect(type);
}
}
}
@Override
public void reset() {
super.reset();
equipmentMonitor.resetChanges();
}
@Override
public List<Message> createUpdateMessage() {
List<Message> messages = super.createUpdateMessage();
for (EquipmentMonitor.Entry change : equipmentMonitor.getChanges()) {
messages.add(new EntityEquipmentMessage(id, change.slot, change.item));
}
return messages;
}
////////////////////////////////////////////////////////////////////////////
// Properties
@Override
public double getEyeHeight() {
return 0;
}
@Override
public double getEyeHeight(boolean ignoreSneaking) {
return getEyeHeight();
}
@Override
public Location getEyeLocation() {
return getLocation().add(0, getEyeHeight(), 0);
}
@Override
public Player getKiller() {
return null;
}
@Override
public boolean hasLineOfSight(Entity other) {
return false;
}
@Override
public EntityEquipment getEquipment() {
return null;
}
////////////////////////////////////////////////////////////////////////////
// Properties
@Override
public int getNoDamageTicks() {
return noDamageTicks;
}
@Override
public void setNoDamageTicks(int ticks) {
noDamageTicks = ticks;
}
@Override
public int getMaximumNoDamageTicks() {
return maxNoDamageTicks;
}
@Override
public void setMaximumNoDamageTicks(int ticks) {
maxNoDamageTicks = ticks;
}
@Override
public int getRemainingAir() {
return airTicks;
}
@Override
public void setRemainingAir(int ticks) {
airTicks = Math.min(ticks, maximumAir);
}
@Override
public int getMaximumAir() {
return maximumAir;
}
@Override
public void setMaximumAir(int ticks) {
maximumAir = Math.max(0, ticks);
}
@Override
public boolean getRemoveWhenFarAway() {
return removeDistance;
}
@Override
public void setRemoveWhenFarAway(boolean remove) {
removeDistance = remove;
}
@Override
public boolean getCanPickupItems() {
return pickupItems;
}
@Override
public void setCanPickupItems(boolean pickup) {
pickupItems = pickup;
}
////////////////////////////////////////////////////////////////////////////
// Line of Sight
private List<Block> getLineOfSight(HashSet<Byte> transparent, int maxDistance, int maxLength) {
// same limit as CraftBukkit
if (maxDistance > 120) {
maxDistance = 120;
}
LinkedList<Block> blocks = new LinkedList<>();
Iterator<Block> itr = new BlockIterator(this, maxDistance);
while (itr.hasNext()) {
Block block = itr.next();
blocks.add(block);
if (maxLength != 0 && blocks.size() > maxLength) {
blocks.removeFirst();
}
int id = block.getTypeId();
if (transparent == null) {
if (id != 0) {
break;
}
} else {
if (!transparent.contains((byte) id)) {
break;
}
}
}
return blocks;
}
@Override
public List<Block> getLineOfSight(HashSet<Byte> transparent, int maxDistance) {
return getLineOfSight(transparent, maxDistance, 0);
}
@Override
public Block getTargetBlock(HashSet<Byte> transparent, int maxDistance) {
return getLineOfSight(transparent, maxDistance, 1).get(0);
}
@Override
public List<Block> getLastTwoTargetBlocks(HashSet<Byte> transparent, int maxDistance) {
return getLineOfSight(transparent, maxDistance, 2);
}
////////////////////////////////////////////////////////////////////////////
// Projectiles
@Override
public Egg throwEgg() {
return launchProjectile(Egg.class);
}
@Override
public Snowball throwSnowball() {
return launchProjectile(Snowball.class);
}
@Override
public Arrow shootArrow() {
return launchProjectile(Arrow.class);
}
@Override
public <T extends Projectile> T launchProjectile(Class<? extends T> projectile) {
return launchProjectile(projectile, getLocation().getDirection()); // todo: multiply by some speed
}
@Override
public <T extends Projectile> T launchProjectile(Class<? extends T> projectile, Vector velocity) {
T entity = world.spawn(getEyeLocation(), projectile);
entity.setVelocity(velocity);
return entity;
}
////////////////////////////////////////////////////////////////////////////
// Health
@Override
public double getHealth() {
return health;
}
@Override
public void setHealth(double health) {
if (health < 0) health = 0;
if (health > maxHealth) health = maxHealth;
this.health = health;
}
@Override
public void damage(double amount) {
damage(amount, null);
}
@Override
public void damage(double amount, Entity source) {
// todo: handle noDamageTicks
lastDamage = amount;
health -= amount;
// todo: death, events, so on
}
@Override
public double getMaxHealth() {
return maxHealth;
}
@Override
public void setMaxHealth(double health) {
maxHealth = health;
}
@Override
public void resetMaxHealth() {
maxHealth = 20;
}
@Override
public double getLastDamage() {
return lastDamage;
}
@Override
public void setLastDamage(double damage) {
lastDamage = damage;
}
////////////////////////////////////////////////////////////////////////////
// Invalid health methods
@Override
public void _INVALID_damage(int amount) {
damage(amount);
}
@Override
public int _INVALID_getLastDamage() {
return (int) getLastDamage();
}
@Override
public void _INVALID_setLastDamage(int damage) {
setLastDamage(damage);
}
@Override
public void _INVALID_setMaxHealth(int health) {
setMaxHealth(health);
}
@Override
public int _INVALID_getMaxHealth() {
return (int) getMaxHealth();
}
@Override
public void _INVALID_damage(int amount, Entity source) {
damage(amount, source);
}
@Override
public int _INVALID_getHealth() {
return (int) getHealth();
}
@Override
public void _INVALID_setHealth(int health) {
setHealth(health);
}
////////////////////////////////////////////////////////////////////////////
// Potion effects
@Override
public boolean addPotionEffect(PotionEffect effect) {
return addPotionEffect(effect, false);
}
@Override
public boolean addPotionEffect(PotionEffect effect, boolean force) {
if (potionEffects.containsKey(effect.getType())) {
if (force) {
removePotionEffect(effect.getType());
} else {
return false;
}
}
potionEffects.put(effect.getType(), effect);
// todo: this, updated, only players in range
/*EntityEffectMessage msg = new EntityEffectMessage(getEntityId(), effect.getType().getId(), effect.getAmplifier(), effect.getDuration());
for (Player player : server.getOnlinePlayers()) {
((GlowPlayer) player).getSession().send(msg);
}*/
return true;
}
@Override
public boolean addPotionEffects(Collection<PotionEffect> effects) {
boolean result = true;
for (PotionEffect effect : effects) {
if (!addPotionEffect(effect)) {
result = false;
}
}
return result;
}
@Override
public boolean hasPotionEffect(PotionEffectType type) {
return potionEffects.containsKey(type);
}
@Override
public void removePotionEffect(PotionEffectType type) {
if (!hasPotionEffect(type)) return;
potionEffects.remove(type);
// todo: this, improved, for players in range
/*EntityRemoveEffectMessage msg = new EntityRemoveEffectMessage(getEntityId(), type.getId());
for (Player player : server.getOnlinePlayers()) {
((GlowPlayer) player).getSession().send(msg);
}*/
}
@Override
public Collection<PotionEffect> getActivePotionEffects() {
return Collections.unmodifiableCollection(potionEffects.values());
}
////////////////////////////////////////////////////////////////////////////
// Custom name
@Override
public void setCustomName(String name) {
customName = name;
}
@Override
public String getCustomName() {
return customName;
}
@Override
public void setCustomNameVisible(boolean flag) {
customNameVisible = flag;
}
@Override
public boolean isCustomNameVisible() {
return customNameVisible;
}
////////////////////////////////////////////////////////////////////////////
// Leashes
@Override
public boolean isLeashed() {
return false;
}
@Override
public Entity getLeashHolder() throws IllegalStateException {
return null;
}
@Override
public boolean setLeashHolder(Entity holder) {
return false;
}
}