diff --git a/src/main/java/net/glowstone/block/blocktype/BlockMagma.java b/src/main/java/net/glowstone/block/blocktype/BlockMagma.java index ab972ee592..e2c398a0b6 100644 --- a/src/main/java/net/glowstone/block/blocktype/BlockMagma.java +++ b/src/main/java/net/glowstone/block/blocktype/BlockMagma.java @@ -1,6 +1,7 @@ package net.glowstone.block.blocktype; import net.glowstone.block.GlowBlock; +import net.glowstone.entity.GlowEntity; import net.glowstone.inventory.ToolType; import org.bukkit.Material; import org.bukkit.entity.LivingEntity; @@ -14,6 +15,10 @@ public BlockMagma() { @Override public void onEntityStep(GlowBlock block, LivingEntity entity) { - entity.damage(1.0, EntityDamageEvent.DamageCause.FIRE); + if (entity instanceof GlowEntity) { + ((GlowEntity) entity).damage(1, block, EntityDamageEvent.DamageCause.FIRE); + } else { + entity.damage(1.0, EntityDamageEvent.DamageCause.FIRE); + } } } diff --git a/src/main/java/net/glowstone/entity/GlowEntity.java b/src/main/java/net/glowstone/entity/GlowEntity.java index fb1be1353c..7daa37d5ab 100644 --- a/src/main/java/net/glowstone/entity/GlowEntity.java +++ b/src/main/java/net/glowstone/entity/GlowEntity.java @@ -982,22 +982,42 @@ public boolean isTouchingMaterial(Material material) { } } } else { - // bounding box-based calculation - Vector min = boundingBox.minCorner; - Vector max = boundingBox.maxCorner; - for (int x = min.getBlockX(); x <= max.getBlockX(); ++x) { - for (int y = min.getBlockY(); y <= max.getBlockY(); ++y) { - for (int z = min.getBlockZ(); z <= max.getBlockZ(); ++z) { - if (world.getBlockTypeIdAt(x, y, z) == material.getId()) { - return true; - } - } + for (Block touchingBlock : getTouchingBlocks()) { + if (touchingBlock.getType().getId() == material.getId()) { + return true; } } } return false; } + /** + * Gets a list containing all the blocks a player is touching + * + *

If the entity has not a defined bounding box, the list will be empty. + * + * @return the list of blocks a player is touching + */ + public List getTouchingBlocks() { + if (boundingBox == null) { + return Collections.emptyList(); + } + + List blocks = new ArrayList<>(); + + // bounding box-based calculation + Vector min = boundingBox.minCorner; + Vector max = boundingBox.maxCorner; + for (int x = min.getBlockX(); x <= max.getBlockX(); ++x) { + for (int y = min.getBlockY(); y <= max.getBlockY(); ++y) { + for (int z = min.getBlockZ(); z <= max.getBlockZ(); ++z) { + blocks.add(world.getBlockAt(x, y, z)); + } + } + } + return blocks; + } + protected final void setBoundingBox(double xz, double y) { boundingBox = new EntityBoundingBox(xz, y); updateBoundingBox(); @@ -1431,7 +1451,7 @@ public void setMetadata(String metadataKey, MetadataValue newMetadataValue) { } public void damage(double amount) { - damage(amount, null, DamageCause.CUSTOM); + damage(amount, (Entity) null, DamageCause.CUSTOM); } public void damage(double amount, Entity source) { @@ -1439,7 +1459,10 @@ public void damage(double amount, Entity source) { } public void damage(double amount, DamageCause cause) { - damage(amount, null, cause); + damage(amount, (Entity) null, cause); + } + + public void damage(double amount, Block block, DamageCause cause) { } public void damage(double amount, Entity source, DamageCause cause) { diff --git a/src/main/java/net/glowstone/entity/GlowLivingEntity.java b/src/main/java/net/glowstone/entity/GlowLivingEntity.java index 842c2bc826..b9b0d6a7d3 100644 --- a/src/main/java/net/glowstone/entity/GlowLivingEntity.java +++ b/src/main/java/net/glowstone/entity/GlowLivingEntity.java @@ -71,6 +71,7 @@ import org.bukkit.entity.Player; import org.bukkit.entity.Projectile; import org.bukkit.event.entity.EntityAirChangeEvent; +import org.bukkit.event.entity.EntityDamageByBlockEvent; import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.event.entity.EntityDamageEvent.DamageCause; @@ -299,14 +300,15 @@ public void pulse() { --noDamageTicks; } - Material mat = getEyeLocation().getBlock().getType(); + Block eyeBlock = getEyeLocation().getBlock(); + Material mat = eyeBlock.getType(); // breathing if (mat == Material.WATER || mat == Material.STATIONARY_WATER) { if (canTakeDamage(DamageCause.DROWNING)) { --remainingAir; if (remainingAir <= -20) { remainingAir = 0; - damage(1, DamageCause.DROWNING); + damage(1, eyeBlock, DamageCause.DROWNING); } } } else { @@ -314,24 +316,30 @@ public void pulse() { } if (isTouchingMaterial(Material.CACTUS)) { - damage(1, DamageCause.CONTACT); + for (Block block : getTouchingBlocks()) { + if (block.getType() == Material.CACTUS) { + damage(1, block, DamageCause.CONTACT); + break; + } + } } + if (location.getY() < -64) { // no canTakeDamage call - pierces through game modes damage(4, DamageCause.VOID); } if (isWithinSolidBlock()) { - damage(1, DamageCause.SUFFOCATION); + damage(1, eyeBlock, DamageCause.SUFFOCATION); } // fire and lava damage if (getLocation().getBlock().getType() == Material.FIRE) { - damage(1, DamageCause.FIRE); + damage(1, getLocation().getBlock(), DamageCause.FIRE); // not applying additional fire ticks after dying in fire stoodInFire = !isDead(); } else if (getLocation().getBlock().getType() == Material.LAVA || getLocation().getBlock().getType() == Material.STATIONARY_LAVA) { - damage(4, DamageCause.LAVA); + damage(4, getLocation().getBlock(), DamageCause.LAVA); if (swamInLava) { setFireTicks(getFireTicks() + 2); } else { @@ -341,7 +349,12 @@ public void pulse() { } else if (isTouchingMaterial(Material.FIRE) || isTouchingMaterial(Material.LAVA) || isTouchingMaterial(Material.STATIONARY_LAVA)) { - damage(1, DamageCause.FIRE); + for (Block block : getTouchingBlocks()) { + if (block.getType() == Material.FIRE || block.getType() == Material.LAVA || block.getType() == Material.STATIONARY_LAVA) { + damage(1, block, DamageCause.CONTACT); + break; + } + } // increment the ticks stood adjacent to fire or lava adjacentBurnTicks++; if (adjacentBurnTicks > 40) { @@ -620,6 +633,17 @@ public boolean canTakeDamage(DamageCause damageCause) { return true; } + /** + * Get whether this entity should take damage from any source. + * + *

Usually used to check environmental sources such as drowning. + * + * @return whether this entity can take damage + */ + public boolean canTakeDamage() { + return noDamageTicks == 0 && health > 0 && !isInvulnerable(); + } + /** * Get whether of not this entity is an arthropod. * @@ -936,10 +960,64 @@ public void setHealth(double health) { } } + private boolean callDamageEvent(EntityDamageEvent event) { + if (event.isCancelled()) { + return true; + } + // apply damage + lastDamage = event.getFinalDamage(); + return false; + } + + private double getEffectiveDamage(double amount) { + // armor damage protection + // formula source: http://minecraft.gamepedia.com/Armor#Damage_Protection + double defensePoints = getAttributeManager().getPropertyValue(Key.KEY_ARMOR); + double toughness = getAttributeManager().getPropertyValue(Key.KEY_ARMOR_TOUGHNESS); + return amount * (1 - Math.min(20.0, + Math.max(defensePoints / 5.0, + defensePoints - amount / (2.0 + toughness / 4.0))) / 25); + } + + @Override + public void damage(double amount, Block block, DamageCause cause) { + if (noDamageTicks > 0 || health <= 0 || isInvulnerable() || !canTakeDamage(cause)) { + return; + } else { + noDamageTicks = maximumNoDamageTicks; + } + + amount = getEffectiveDamage(amount); + + + // fire event + EntityDamageByBlockEvent event = EventFactory.getInstance().onEntityDamage( + new EntityDamageByBlockEvent(block, this, cause, amount)); + + if (callDamageEvent(event)) { + return; + } + + // apply damage + amount = event.getFinalDamage(); + lastDamage = amount; + + setHealth(health - amount); + playEffectKnownAndSelf(EntityEffect.HURT); + + // play sounds, handle death + if (health > 0) { + Sound hurtSound = getHurtSound(); + if (hurtSound != null && !isSilent()) { + world.playSound(location, hurtSound, getSoundVolume(), getSoundPitch()); + } + } + } + @Override public void damage(double amount, Entity source, DamageCause cause) { // invincibility timer - if (noDamageTicks > 0 || health <= 0 || !canTakeDamage(cause) || isInvulnerable()) { + if (noDamageTicks > 0 || health <= 0 || isInvulnerable() || !canTakeDamage(cause)) { return; } else { noDamageTicks = maximumNoDamageTicks; @@ -962,13 +1040,7 @@ public void damage(double amount, Entity source, DamageCause cause) { } } - // armor damage protection - // formula source: http://minecraft.gamepedia.com/Armor#Damage_Protection - double defensePoints = getAttributeManager().getPropertyValue(Key.KEY_ARMOR); - double toughness = getAttributeManager().getPropertyValue(Key.KEY_ARMOR_TOUGHNESS); - amount = amount * (1 - Math.min(20.0, - Math.max(defensePoints / 5.0, - defensePoints - amount / (2.0 + toughness / 4.0))) / 25); + amount = getEffectiveDamage(amount); // fire event EntityDamageEvent event = EventFactory.getInstance().onEntityDamage(source == null