Skip to content
This repository has been archived by the owner on Nov 9, 2017. It is now read-only.

Commit

Permalink
Implemented sponge block functionality.
Browse files Browse the repository at this point in the history
Dry sponges absorb water that can be found within a 7 block taxicab
distance. Due to the specific implementation, a taxicab block iterator has
been added to help assist in the more complex calculation of which blocks
to absorb. The iterator has been designed for reusability.
  • Loading branch information
Marcos Vives Del Sol authored and SpaceManiac committed Dec 22, 2014
1 parent 87fa006 commit 6e86a71
Show file tree
Hide file tree
Showing 5 changed files with 195 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/main/java/net/glowstone/block/ItemTable.java
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ private void registerBuiltins() {
reg(Material.CAULDRON, new BlockDirectDrops(Material.CAULDRON_ITEM, ToolType.PICKAXE));
reg(Material.STANDING_BANNER, new BlockBanner());
reg(Material.WALL_BANNER, new BlockBanner());
reg(Material.SPONGE, new BlockSponge());

reg(Material.SIGN, new ItemSign());
reg(Material.REDSTONE, new ItemPlaceAs(Material.REDSTONE_WIRE));
Expand Down
54 changes: 54 additions & 0 deletions src/main/java/net/glowstone/block/blocktype/BlockSponge.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package net.glowstone.block.blocktype;

import com.google.common.collect.Sets;
import net.glowstone.block.GlowBlock;
import net.glowstone.block.GlowBlockState;
import net.glowstone.entity.GlowPlayer;
import net.glowstone.util.BlockMaterialValidator;
import net.glowstone.util.TaxicabBlockIterator;
import org.bukkit.Material;
import org.bukkit.SpongeType;
import org.bukkit.block.BlockFace;
import org.bukkit.inventory.ItemStack;
import org.bukkit.material.MaterialData;
import org.bukkit.material.Sponge;
import org.bukkit.util.Vector;

import java.util.Set;

public class BlockSponge extends BlockType {

private static final Set<Material> WATER_MATERIALS = Sets.immutableEnumSet(Material.WATER, Material.STATIONARY_WATER);

@Override
public void placeBlock(GlowPlayer player, GlowBlockState state, BlockFace face, ItemStack holding, Vector clickedLoc) {
// TODO: Move this to a new method when physics works and run this on neighbour change too.

MaterialData data = holding.getData();
if (!(data instanceof Sponge)) {
warnMaterialData(Sponge.class, data);
return;
}
Sponge sponge = (Sponge) data;

if (sponge.getType() == SpongeType.NORMAL) {
GlowBlock block = state.getBlock();

TaxicabBlockIterator iterator = new TaxicabBlockIterator(block);
iterator.setMaxDistance(7);
iterator.setMaxBlocks(66);
iterator.setValidator(new BlockMaterialValidator(WATER_MATERIALS));

if (iterator.hasNext()) {
sponge = sponge.clone();
sponge.setType(SpongeType.WET);
do {
iterator.next().setType(Material.AIR);
} while (iterator.hasNext());
}
}

state.setType(Material.SPONGE);
state.setData(sponge);
}
}
21 changes: 21 additions & 0 deletions src/main/java/net/glowstone/util/BlockMaterialValidator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package net.glowstone.util;

import org.bukkit.Material;
import org.bukkit.block.Block;

import java.util.Set;

public class BlockMaterialValidator implements Validator<Block> {

private final Set<Material> validMaterials;

public BlockMaterialValidator(Set<Material> validMaterials) {
this.validMaterials = validMaterials;
}

@Override
public boolean isValid(Block block) {
return validMaterials.contains(block.getType());
}

}
102 changes: 102 additions & 0 deletions src/main/java/net/glowstone/util/TaxicabBlockIterator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package net.glowstone.util;

import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;

import java.util.*;

public class TaxicabBlockIterator implements Iterator<Block> {

private static final BlockFace[] VALID_FACES = new BlockFace[]{
BlockFace.DOWN, BlockFace.UP, BlockFace.NORTH, BlockFace.SOUTH, BlockFace.WEST, BlockFace.EAST
};

private final Queue<Object> pendingAnalysis = new LinkedList<>();
private final Queue<Block> nextValidBlocks = new LinkedList<>();
private final Set<Block> usedBlocks = new HashSet<>();
private int currentDistance = 1;
private int validBlockCount = 0;

private int maxDistance = Integer.MAX_VALUE;
private int maxBlocks = Integer.MAX_VALUE;
private Validator<Block> validator;

public TaxicabBlockIterator(Block origin) {
pendingAnalysis.add(origin);
pendingAnalysis.add(DistanceMarker.INSTANCE);
usedBlocks.add(origin);
}

public void setMaxDistance(int maxDistance) {
this.maxDistance = maxDistance;
}

public void setMaxBlocks(int maxBlocks) {
this.maxBlocks = maxBlocks;
}

public void setValidator(Validator<Block> validator) {
this.validator = validator;
}

private boolean isValid(Block block) {
return validator == null || validator.isValid(block);
}

@Override
public boolean hasNext() {
if (validBlockCount >= maxBlocks) {
return false;
}

// Keep going till the valid block queue contains something, we reach the distance limit, or we empty the pending analysis queue.
// Note that the pending analysis queue will always contain at least one element: the end of distance marker.
while (nextValidBlocks.isEmpty() && currentDistance <= maxDistance && pendingAnalysis.size() >= 2) {
Object object = pendingAnalysis.remove();

// If we find the end of distance marker, we'll increase the distance, and then we'll re-add it to the end.
if (object == DistanceMarker.INSTANCE) {
pendingAnalysis.add(object);
currentDistance++;
continue;
}

// If it wasn't the EoD marker, it must be a block. We'll look now for valid blocks around it.
Block block = (Block) object;
for (BlockFace face : VALID_FACES) {
Block near = block.getRelative(face);

// Only analyse the block if we haven't checked it yet.
if (usedBlocks.add(near) && isValid(near)) {
nextValidBlocks.add(near);
pendingAnalysis.add(near);
}
}
}

return !nextValidBlocks.isEmpty();
}

@Override
public Block next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
validBlockCount++;
return nextValidBlocks.remove();
}

@Override
public void remove() {
throw new UnsupportedOperationException();
}

private static final class DistanceMarker {

public static final DistanceMarker INSTANCE = new DistanceMarker();

private DistanceMarker() {
}

}
}
17 changes: 17 additions & 0 deletions src/main/java/net/glowstone/util/Validator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package net.glowstone.util;

/**
* Represents a simple validator to compare objects on a true / false basis.
* @param <E> The type of object to compare.
*/
public interface Validator<E> {

/**
* Determines if the supplied object is valid as oer the implementation
* rules defined.
* @param object The object to validate
* @return True if valid, false otherwise
*/
boolean isValid(E object);

}

0 comments on commit 6e86a71

Please sign in to comment.