/* * Copyright or © or Copr. AmauryCarrade (2015) * * http://amaury.carrade.eu * * This software is governed by the CeCILL-B license under French law and * abiding by the rules of distribution of free software. You can use, * modify and/ or redistribute the software under the terms of the CeCILL-B * license as circulated by CEA, CNRS and INRIA at the following URL * "http://www.cecill.info". * * As a counterpart to the access to the source code and rights to copy, * modify and redistribute granted by the license, users are provided only * with a limited warranty and the software's author, the holder of the * economic rights, and the successive licensors have only limited * liability. * * In this respect, the user's attention is drawn to the risks associated * with loading, using, modifying and/or developing or reproducing the * software by the user in light of its specific status of free software, * that may mean that it is complicated to manipulate, and that also * therefore means that it is reserved for developers and experienced * professionals having in-depth computer knowledge. Users are therefore * encouraged to load and test the software's suitability as regards their * requirements in conditions enabling the security of their systems and/or * data to be ensured and, more generally, to use and operate it in the * same conditions as regards security. * * The fact that you are presently reading this means that you have had * knowledge of the CeCILL-B license and that you accept its terms. */ package eu.carrade.amaury.UHCReloaded.game; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.material.MaterialData; import java.util.HashMap; import java.util.Map; public class Cage { private final Location baseLocation; private Material material = Material.BARRIER; private MaterialData materialData = null; private final boolean buildCeiling; private final boolean visibleWalls; private int radius = 1; private int internalHeight = 3; private boolean built = false; private Map<Location, SimpleBlock> blocksBuilt = new HashMap<>(); /** * @param baseLocation The cage base location (where a player can be * teleported to be on the ground). * @param buildCeiling {@code true} to build the ceiling of the cage. */ public Cage(Location baseLocation, boolean buildCeiling, boolean visibleWalls) { this.baseLocation = baseLocation; this.buildCeiling = buildCeiling; this.visibleWalls = visibleWalls; } /** * Sets the custom material to use. * * @param customMaterial A material. * @param data The data value (or {@code null}). */ public void setCustomMaterial(Material customMaterial, MaterialData data) { this.material = customMaterial == null ? Material.BARRIER : customMaterial; this.materialData = data; } /** * Sets the custom material to use. * * @param customMaterial A material. * @param data The data value. */ public void setCustomMaterial(Material customMaterial, byte data) { setCustomMaterial(customMaterial, new MaterialData(this.material, data)); } /** * Sets the custom material to use. * * @param customMaterial A material. */ public void setCustomMaterial(Material customMaterial) { setCustomMaterial(customMaterial, null); } /** * Sets the internal height, i.e. the height of the open space for players * (the ceiling will be above this height, and the ground under). * * @param internalHeight The height. */ public void setInternalHeight(int internalHeight) { this.internalHeight = internalHeight; } /** * Sets the square radius of the cage. * * With 0, you'll have a cage with one block to walk. With 1, you'll have a * 3×3 cage. With 2, a 5×5 cage. Etc. * * @param radius The radius. */ public void setRadius(int radius) { this.radius = radius; } /** * Sets a block (and remembers the old one to clean up things after). * * @param location The location * @param material The block material */ private void setBlock(final Location location, final Material material) { setBlock(location, material, null); } /** * Sets a block (and remembers the old one to clean up things after). * * @param location The location * @param material The block material * @param data The block data value (as byte) */ private void setBlock(final Location location, final Material material, final byte data) { setBlock(location, material, new MaterialData(material, data)); } /** * Sets a block (and remembers the old one to clean up things after). * * @param location The location * @param material The block material * @param data The block data value (as {@link MaterialData}) */ private void setBlock(final Location location, final Material material, final MaterialData data) { final Block block = location.getBlock(); if (!blocksBuilt.containsKey(location)) blocksBuilt.put(location, new SimpleBlock(block.getType(), block.getState().getData().clone())); block.setType(material); if (data != null) block.setData(data.getData()); } /** * Builds the cage. */ public void build() { if (built) return; final int externalRadius = radius + 1; final int xMin = baseLocation.getBlockX() - externalRadius; final int xMax = baseLocation.getBlockX() + externalRadius; final int zMin = baseLocation.getBlockZ() - externalRadius; final int zMax = baseLocation.getBlockZ() + externalRadius; final World world = baseLocation.getWorld(); // Builds the base barrier square under any cage, to support falling blocks and to avoid players falling // through the blocks when teleported for (int x = xMin; x <= xMax; x++) for (int z = zMin; z <= zMax; z++) setBlock(new Location(world, x, baseLocation.getBlockY() - 2, z), Material.BARRIER); // Builds the ground for (int x = xMin + 1; x <= xMax - 1; x++) for (int z = zMin + 1; z <= zMax - 1; z++) setBlock(new Location(world, x, baseLocation.getBlockY() - 1, z), material, materialData); // Builds the walls final Material wallsMaterial = visibleWalls ? material : Material.BARRIER; final MaterialData wallsMaterialData = visibleWalls ? materialData : null; for (int x = xMin; x <= xMax; x++) { for (int y = baseLocation.getBlockY() - 1; y < baseLocation.getBlockY() + internalHeight; y++) { setBlock(new Location(world, x, y, zMin), wallsMaterial, wallsMaterialData); setBlock(new Location(world, x, y, zMax), wallsMaterial, wallsMaterialData); } } for (int z = zMin; z <= zMax; z++) { for (int y = baseLocation.getBlockY() - 1; y < baseLocation.getBlockY() + internalHeight; y++) { setBlock(new Location(world, xMin, y, z), wallsMaterial, wallsMaterialData); setBlock(new Location(world, xMax, y, z), wallsMaterial, wallsMaterialData); } } // Builds the ceiling final Material ceilingMaterial = buildCeiling ? material : Material.BARRIER; final MaterialData ceilingMaterialData = buildCeiling ? materialData : null; int xMinCeiling = xMin, xMaxCeiling = xMax, zMinCeiling = zMin, zMaxCeiling = zMax; if (buildCeiling && !visibleWalls) { xMinCeiling++; xMaxCeiling--; zMinCeiling++; zMaxCeiling--; } for (int x = xMinCeiling; x <= xMaxCeiling; x++) for (int z = zMinCeiling; z <= zMaxCeiling; z++) setBlock(new Location(world, x, baseLocation.getBlockY() + internalHeight, z), ceilingMaterial, ceilingMaterialData); built = true; } /** * Destroys the cage. */ public void destroy() { for (Map.Entry<Location, SimpleBlock> entry : blocksBuilt.entrySet()) { final Block block = entry.getKey().getBlock(); final SimpleBlock originalBlock = entry.getValue(); block.setType(originalBlock.material); if (originalBlock.data != null) block.getState().setData(originalBlock.data); } built = false; } /** * Cage type, enum used for the configuration */ public enum CageType { /** * Cages in stained glass, using the team color (or the closest color * available). */ TEAM_COLOR_TRANSPARENT, /** * Cages in stained hardened clay, using the team color (or the closest * color available). */ TEAM_COLOR_SOLID, /** * Cages in a custom provided block. */ CUSTOM } /** * A block + data value (storage class used to restore old blocks when the * cage is destroyed). */ private class SimpleBlock { public Material material; public MaterialData data; public SimpleBlock(Material material, MaterialData data) { this.material = material; this.data = data; } } }