package net.glowstone.entity; import com.flowpowered.networking.Message; import net.glowstone.EventFactory; import net.glowstone.GlowWorld; import net.glowstone.block.GlowBlock; import net.glowstone.entity.physics.BoundingBox; import net.glowstone.net.message.play.entity.SpawnLightningStrikeMessage; import net.glowstone.util.Position; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.Sound; import org.bukkit.block.BlockFace; import org.bukkit.block.BlockState; import org.bukkit.entity.Damageable; import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; import org.bukkit.entity.LightningStrike; import org.bukkit.event.block.BlockIgniteEvent; import org.bukkit.event.block.BlockIgniteEvent.IgniteCause; import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.util.Vector; import java.util.Arrays; import java.util.List; import java.util.Random; /** * A GlowLightning strike is an entity produced during thunderstorms. */ public class GlowLightningStrike extends GlowWeather implements LightningStrike { /** * Whether the lightning strike is just for effect. */ private boolean effect; /** * How long this lightning strike has to remain in the world. */ private final int ticksToLive; private final Random random; public GlowLightningStrike(Location location, boolean effect, Random random) { super(location); this.effect = effect; this.ticksToLive = 30; this.random = random; } @Override public EntityType getType() { return EntityType.LIGHTNING; } @Override public boolean isEffect() { return effect; } @Override public void pulse() { super.pulse(); if (getTicksLived() >= ticksToLive) { remove(); } if (getTicksLived() == 1) { final GlowWorld world = (GlowWorld) location.getWorld(); // Play Sound world.playSound(location, Sound.AMBIENCE_THUNDER, 10000, 0.8F + random.nextFloat() * 0.2F); world.playSound(location, Sound.EXPLODE, 2, 0.5F + random.nextFloat() * 0.2F); if (!effect) { // if it's not just a visual effect // set target block on fire if required if (world.getGameRuleMap().getBoolean("doFireTick")) { GlowBlock block = world.getBlockAt(location); setBlockOnFire(block); for (int i = 0; i < 4; i++) { int x = location.getBlockX() - 1 + random.nextInt(3); int z = location.getBlockZ() - 1 + random.nextInt(3); int y = location.getBlockY() - 1 + random.nextInt(3); block = world.getBlockAt(x, y, z); setBlockOnFire(block); } } // deal damage to nearby entities for (Entity entity : getNearbyEntities(3, 6, 3)) { if (entity instanceof Damageable) { ((Damageable) entity).damage(5, this, EntityDamageEvent.DamageCause.LIGHTNING); } entity.setFireTicks(entity.getMaxFireTicks()); } } } } @Override public List<Message> createSpawnMessage() { int x = Position.getIntX(location); int y = Position.getIntY(location); int z = Position.getIntZ(location); return Arrays.<Message>asList(new SpawnLightningStrikeMessage(id, x, y, z)); } @Override public List<Message> createUpdateMessage() { return Arrays.asList(); } @Override public List<Entity> getNearbyEntities(double x, double y, double z) { // This behavior is similar to CraftBukkit, where a call with args // (0, 0, 0) finds any entities whose bounding boxes intersect that of // this entity. final BoundingBox searchBox = BoundingBox.fromPositionAndSize(location.toVector(), new Vector(0, 0, 0)); final Vector vec = new Vector(x, y, z); final Vector vec2 = new Vector(0, 0.5 * y, 0); searchBox.minCorner.subtract(vec).add(vec2); searchBox.maxCorner.add(vec).add(vec2); return world.getEntityManager().getEntitiesInside(searchBox, this); } private void setBlockOnFire(GlowBlock block) { if (block.isEmpty() && block.getRelative(BlockFace.DOWN).isFlammable()) { BlockIgniteEvent igniteEvent = new BlockIgniteEvent(block, IgniteCause.LIGHTNING, this); EventFactory.callEvent(igniteEvent); if (!igniteEvent.isCancelled()) { final BlockState state = block.getState(); state.setType(Material.FIRE); state.update(true); } } } public LightningStrike.Spigot spigot() { return null; } }