This repository has been archived by the owner on May 3, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 28
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
updates and release for 0.0.1-preR10
- Loading branch information
1 parent
85f796f
commit 50d9f69
Showing
82 changed files
with
2,505 additions
and
745 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,345 @@ | ||
package net.glowstone; | ||
|
||
import net.glowstone.block.GlowBlock; | ||
import net.glowstone.block.ItemTable; | ||
import net.glowstone.block.blocktype.BlockTNT; | ||
import net.glowstone.entity.GlowEntity; | ||
import net.glowstone.entity.GlowHumanEntity; | ||
import net.glowstone.entity.GlowLivingEntity; | ||
import net.glowstone.entity.GlowPlayer; | ||
import net.glowstone.net.message.play.game.ExplosionMessage; | ||
import org.bukkit.*; | ||
import org.bukkit.block.Block; | ||
import org.bukkit.block.BlockFace; | ||
import org.bukkit.entity.Entity; | ||
import org.bukkit.entity.EntityType; | ||
import org.bukkit.entity.LivingEntity; | ||
import org.bukkit.event.block.BlockIgniteEvent; | ||
import org.bukkit.event.entity.EntityDamageEvent; | ||
import org.bukkit.event.entity.EntityExplodeEvent; | ||
import org.bukkit.util.BlockVector; | ||
import org.bukkit.util.Vector; | ||
|
||
import java.util.*; | ||
|
||
public final class Explosion { | ||
|
||
public static final int POWER_TNT = 4; | ||
public static final int POWER_BED = 5; | ||
public static final int POWER_CREEPER = 3; | ||
public static final int POWER_CHARGED_CREEPER = 6; | ||
public static final int POWER_GHAST = 1; | ||
public static final int POWER_WITHER_SKULL = 1; | ||
public static final int POWER_WITHER_CREATION = 7; | ||
public static final int POWER_ENDER_CRYSTAL = 6; | ||
|
||
private float power; | ||
private final Entity source; | ||
private final Location location; | ||
private final boolean incendiary; | ||
private final boolean breakBlocks; | ||
private final GlowWorld world; | ||
private float yield = 0.3f; | ||
|
||
private static final Random random = new Random(); | ||
private final ItemTable itemTable; | ||
|
||
/** | ||
* Creates a new explosion | ||
* @param source The entity causing this explosion | ||
* @param world The world this explosion is in | ||
* @param x The X location of the explosion | ||
* @param y The Y location of the explosion | ||
* @param z The Z location of the explosion | ||
* @param power The power of the explosion | ||
* @param incendiary Whether or not blocks should be set on fire | ||
* @param breakBlocks Whether blocks should break through this explosion | ||
*/ | ||
public Explosion(Entity source, GlowWorld world, double x, double y, double z, float power, boolean incendiary, boolean breakBlocks) { | ||
this(source, new Location(world, x, y, z), power, incendiary, breakBlocks); | ||
} | ||
|
||
/** | ||
* Creates a new explosion | ||
* @param source The entity causing this explosion | ||
* @param location The location this explosion is occuring at. Must contain a GlowWorld | ||
* @param power The power of the explosion | ||
* @param incendiary Whether or not blocks should be set on fire | ||
* @param breakBlocks Whether blocks should break through this explosion | ||
*/ | ||
public Explosion(Entity source, Location location, float power, boolean incendiary, boolean breakBlocks) { | ||
if (!(location.getWorld() instanceof GlowWorld)) { | ||
throw new IllegalArgumentException("Supplied location does not have a valid GlowWorld"); | ||
} | ||
|
||
this.source = source; | ||
this.location = location.clone(); | ||
this.power = power; | ||
this.incendiary = incendiary; | ||
this.breakBlocks = breakBlocks; | ||
this.world = (GlowWorld) location.getWorld(); | ||
itemTable = ItemTable.instance(); | ||
} | ||
|
||
public boolean explodeWithEvent() { | ||
if (power < 0.1f) | ||
return true; | ||
|
||
Set<BlockVector> droppedBlocks = calculateBlocks(); | ||
|
||
EntityExplodeEvent event = EventFactory.callEvent(new EntityExplodeEvent(source, location, toBlockList(droppedBlocks), yield)); | ||
if (event.isCancelled()) return false; | ||
|
||
this.yield = event.getYield(); | ||
|
||
playOutSoundAndParticles(); | ||
|
||
List<Block> blocks = toBlockList(droppedBlocks); | ||
|
||
for (Block block : blocks) { | ||
handleBlockExplosion((GlowBlock) block); | ||
} | ||
|
||
if (incendiary) { | ||
for (Block block : blocks) { | ||
setBlockOnFire((GlowBlock) block); | ||
} | ||
} | ||
|
||
Collection<GlowPlayer> affectedPlayers = damageEntities(); | ||
for (GlowPlayer player : affectedPlayers) { | ||
playOutExplosion(player, droppedBlocks); | ||
} | ||
|
||
return true; | ||
} | ||
|
||
/////////////////////////////////////////////////// | ||
// Calculate all the dropping blocks | ||
|
||
private Set<BlockVector> calculateBlocks() { | ||
if (!breakBlocks) | ||
return new HashSet<>(); | ||
|
||
Set<BlockVector> blocks = new HashSet<>(); | ||
|
||
final int value = 16; | ||
|
||
for (int x = 0; x < value; x++) { | ||
for (int y = 0; y < value; y++) { | ||
for (int z = 0; z < value; z++) { | ||
if (!(x == 0 || x == value - 1 || y == 0 || y == value - 1 || z == 0 || z == value - 1)) { | ||
continue; | ||
} | ||
calculateRay(x, y, z, blocks); | ||
} | ||
} | ||
} | ||
|
||
return blocks; | ||
} | ||
|
||
private void calculateRay(int ox, int oy, int oz, Collection<BlockVector> result) { | ||
double x = ox / 7.5 - 1; | ||
double y = oy / 7.5 - 1; | ||
double z = oz / 7.5 - 1; | ||
Vector direction = new Vector(x, y, z); | ||
direction.normalize(); | ||
direction.multiply(0.3f); // 0.3 blocks away with each step | ||
|
||
Location current = location.clone(); | ||
|
||
float currentPower = calculateStartPower(); | ||
|
||
while (currentPower > 0) { | ||
GlowBlock block = world.getBlockAt(current); | ||
|
||
if (block.getType() != Material.AIR) { | ||
double blastDurability = getBlastDurability(block) / 5d; | ||
blastDurability += 0.3F; | ||
blastDurability *= 0.3F; | ||
currentPower -= blastDurability; | ||
|
||
if (currentPower > 0) { | ||
result.add(new BlockVector(block.getX(), block.getY(), block.getZ())); | ||
} | ||
} | ||
|
||
current.add(direction); | ||
currentPower -= 0.225f; | ||
} | ||
} | ||
|
||
private void handleBlockExplosion(GlowBlock block) { | ||
if (block.getType() == Material.AIR) { | ||
return; | ||
} else if (block.getType() == Material.TNT) { | ||
BlockTNT.igniteBlock(block, true); | ||
return; | ||
} | ||
|
||
block.breakNaturally(yield); | ||
} | ||
|
||
private float calculateStartPower() { | ||
float rand = random.nextFloat(); | ||
rand *= 0.6F; // (max - 0.7) | ||
rand += 0.7; // min | ||
return rand * power; | ||
} | ||
|
||
private double getBlastDurability(GlowBlock block) { | ||
// TODO: return the block's blast durability | ||
return 2.5; | ||
} | ||
|
||
private List<Block> toBlockList(Collection<BlockVector> locs) { | ||
List<Block> blocks = new ArrayList<>(locs.size()); | ||
for (BlockVector location : locs) | ||
blocks.add(world.getBlockAt(location.getBlockX(), location.getBlockY(), location.getBlockZ())); | ||
return blocks; | ||
} | ||
|
||
private void setBlockOnFire(GlowBlock block) { | ||
if (random.nextInt(3) != 0) | ||
return; | ||
|
||
Block below = block.getRelative(BlockFace.DOWN); | ||
// TODO: check for flammable blocks | ||
Material belowType = below.getType(); | ||
if (belowType == Material.AIR || belowType == Material.FIRE) return; | ||
|
||
BlockIgniteEvent event = EventFactory.callEvent(new BlockIgniteEvent(block, BlockIgniteEvent.IgniteCause.EXPLOSION, source)); | ||
if (event.isCancelled()) | ||
return; | ||
|
||
block.setType(Material.FIRE); | ||
} | ||
|
||
///////////////////////////////////////// | ||
// Damage entities | ||
|
||
private Collection<GlowPlayer> damageEntities() { | ||
float power = this.power; | ||
this.power *= 2f; | ||
|
||
Collection<GlowPlayer> affectedPlayers = new ArrayList<>(); | ||
|
||
Collection<GlowLivingEntity> entities = getNearbyEntities(); | ||
for (GlowLivingEntity entity : entities) { | ||
double disDivPower = distanceTo(entity) / (double) this.power; | ||
if (disDivPower > 1.0D) continue; | ||
|
||
Vector vecDistance = distanceToHead(entity); | ||
if (vecDistance.length() == 0.0) continue; | ||
|
||
vecDistance.normalize(); | ||
|
||
double basicDamage = calculateDamage(entity, disDivPower); | ||
int explosionDamage = (int) ((basicDamage * basicDamage + basicDamage) * 4 * (double) power + 1.0D); | ||
|
||
if (!(entity instanceof GlowHumanEntity && ((GlowHumanEntity) entity).getGameMode() == GameMode.CREATIVE)) { | ||
EntityDamageEvent.DamageCause damageCause; | ||
if (source == null || source.getType() == EntityType.PRIMED_TNT) { | ||
damageCause = EntityDamageEvent.DamageCause.BLOCK_EXPLOSION; | ||
} else { | ||
damageCause = EntityDamageEvent.DamageCause.ENTITY_EXPLOSION; | ||
} | ||
entity.damage(explosionDamage, source, damageCause); | ||
} | ||
|
||
double enchantedDamage = calculateEnchantedDamage(basicDamage, entity); | ||
vecDistance.multiply(enchantedDamage); | ||
|
||
Vector currentVelocity = entity.getVelocity(); | ||
currentVelocity.add(vecDistance); | ||
entity.setVelocity(currentVelocity); | ||
|
||
if (entity instanceof GlowPlayer) { | ||
affectedPlayers.add((GlowPlayer) entity); | ||
} | ||
} | ||
|
||
this.power = power; | ||
|
||
return affectedPlayers; | ||
} | ||
|
||
private double calculateEnchantedDamage(double basicDamage, GlowLivingEntity entity) { | ||
int level = 0; // TODO: calculate explosion protection level of entity's equipment | ||
|
||
if (level > 0) { | ||
float sub = level * 0.15f; | ||
double damage = basicDamage * sub; | ||
damage = Math.floor(damage); | ||
return basicDamage - damage; | ||
} | ||
|
||
return basicDamage; | ||
} | ||
|
||
private double calculateDamage(GlowEntity entity, double disDivPower) { | ||
double damage = world.rayTrace(location, entity); | ||
return (damage * (1D - disDivPower)); | ||
} | ||
|
||
private Collection<GlowLivingEntity> getNearbyEntities() { | ||
// TODO: fetch only necessary entities | ||
List<LivingEntity> entities = world.getLivingEntities(); | ||
List<GlowLivingEntity> nearbyEntities = new ArrayList<>(); | ||
|
||
for (LivingEntity entity : entities) { | ||
if (distanceTo(entity) / (double) power < 1.) { | ||
nearbyEntities.add((GlowLivingEntity) entity); | ||
} | ||
} | ||
|
||
return nearbyEntities; | ||
} | ||
|
||
private double distanceTo(LivingEntity entity) { | ||
return location.clone().subtract(entity.getLocation()).length(); | ||
} | ||
|
||
private Vector distanceToHead(LivingEntity entity) { | ||
return entity.getLocation().clone().subtract(location.clone().subtract(0, entity.getEyeHeight(), 0)).toVector(); | ||
} | ||
|
||
/////////////////////////////////////// | ||
// Visualize | ||
private void playOutSoundAndParticles() { | ||
world.playSound(location, Sound.EXPLODE, 4, (1.0F + (random.nextFloat() - random.nextFloat()) * 0.2F) * 0.7F); | ||
|
||
if (this.power >= 2.0F && this.breakBlocks) { | ||
// send huge explosion | ||
world.showParticle(location, Particle.EXPLOSION_HUGE, 0, 0, 0, 0, 0); | ||
} else { | ||
// send large explosion | ||
world.showParticle(location, Particle.EXPLOSION_LARGE, 0, 0, 0, 0, 0); | ||
} | ||
} | ||
|
||
private void playOutExplosion(GlowPlayer player, Iterable<BlockVector> blocks) { | ||
Collection<ExplosionMessage.Record> records = new ArrayList<>(); | ||
|
||
Location clientLoc = location.clone(); | ||
clientLoc.setX((int) clientLoc.getX()); | ||
clientLoc.setY((int) clientLoc.getY()); | ||
clientLoc.setZ((int) clientLoc.getZ()); | ||
|
||
for (BlockVector block : blocks) { | ||
byte x = (byte) (block.getBlockX() - clientLoc.getBlockX()); | ||
byte y = (byte) (block.getBlockY() - clientLoc.getBlockY()); | ||
byte z = (byte) (block.getBlockZ() - clientLoc.getBlockZ()); | ||
records.add(new ExplosionMessage.Record(x, y, z)); | ||
} | ||
|
||
Vector velocity = player.getVelocity(); | ||
ExplosionMessage message = new ExplosionMessage((float) location.getX(), (float) location.getY(), (float) location.getZ(), | ||
5, | ||
(float) velocity.getX(), (float) velocity.getY(), (float) velocity.getZ(), | ||
records); | ||
|
||
player.getSession().send(message); | ||
} | ||
} |
Oops, something went wrong.