package net.glowstone.block.blocktype; import net.glowstone.GlowWorld; import net.glowstone.block.GlowBlock; import net.glowstone.block.GlowBlockState; import net.glowstone.entity.GlowPlayer; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.block.Biome; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.entity.Creature; import org.bukkit.entity.Entity; import org.bukkit.entity.LivingEntity; import org.bukkit.inventory.ItemStack; import org.bukkit.material.Bed; import org.bukkit.material.MaterialData; import org.bukkit.util.Vector; public class BlockBed extends BlockType { public BlockBed() { setDrops(new ItemStack(Material.BED)); } @Override public void placeBlock(GlowPlayer player, GlowBlockState state, BlockFace face, ItemStack holding, Vector clickedLoc) { BlockFace direction = getOppositeBlockFace(player.getLocation(), false).getOppositeFace(); if (state.getBlock().getRelative(direction).getType() == Material.AIR && state.getBlock().getRelative(direction).getRelative(BlockFace.DOWN).getType().isSolid()) { super.placeBlock(player, state, face, holding, clickedLoc); final MaterialData data = state.getData(); if (data instanceof Bed) { ((Bed) data).setFacingDirection(direction); state.setData(data); } else { warnMaterialData(Bed.class, data); } } } @Override public void afterPlace(GlowPlayer player, GlowBlock block, ItemStack holding, GlowBlockState oldState) { if (block.getType() == Material.BED_BLOCK) { BlockFace direction = ((Bed) block.getState().getData()).getFacing(); GlowBlock headBlock = block.getRelative(direction); headBlock.setType(Material.BED_BLOCK); GlowBlockState headBlockState = headBlock.getState(); MaterialData data = headBlockState.getData(); ((Bed) data).setHeadOfBed(true); ((Bed) data).setFacingDirection(direction); headBlockState.setData(data); headBlockState.update(true); } } @Override public boolean blockInteract(GlowPlayer player, GlowBlock block, BlockFace face, Vector clickedLoc) { GlowWorld world = player.getWorld(); MaterialData data = block.getState().getData(); if (!(data instanceof Bed)) { warnMaterialData(Bed.class, data); return false; } block = getHead(block); // Disallow sleeping in nether and end biomes Biome biome = block.getBiome(); if (biome == Biome.HELL || biome == Biome.SKY) { // Set off an explosion at the bed slightly stronger than TNT world.createExplosion(block.getLocation(), 5F, true); return true; } // Sleeping is only possible during the night or a thunderstorm // Tick values for day/night time taken from the minecraft wiki if (world.getTime() < 12541 || world.getTime() > 23458 || world.isThundering()) { player.sendMessage("You can only sleep at night"); return true; } if (isOccupied(block)) { player.sendMessage("This bed is occupied."); return true; } if (!isWithinDistance(player, block, 3, 2, 3)) { return true; // Distance between player and bed is too great, fail silently } for (LivingEntity e : world.getLivingEntities()) { // Check for hostile mobs relative to the block below the head of the bed if (e instanceof Creature && isWithinDistance(e, block.getRelative(BlockFace.DOWN), 8, 5, 8)) { player.sendMessage("You may not rest now, there are monsters nearby"); return true; } } player.enterBed(block); return true; } /** * Helper method for set whether the specified bed blocks are occupied. * @param head head of the bed * @param foot foot of the bed */ public static void setOccupied(GlowBlock head, GlowBlock foot, boolean occupied) { byte headData = head.getData(); byte footData = foot.getData(); head.setData((byte) (occupied ? (headData | 0x4) : (headData & ~0x4))); foot.setData((byte) (occupied ? (footData | 0x4) : (footData & ~0x4))); } /** * Return whether the specified bed block is occupied. * @param block part of the bed * @return true if this bed is occupied, false if it is not */ public static boolean isOccupied(GlowBlock block) { return (block.getData() & 0x4) == 0x4; } /** * Checks whether the entity is within the specified distance from the block. * @param entity the entity * @param block the block * @param x maximum distance on x axis * @param y maximum distance on y axis * @param z maximum distance on z axis * @return Whether the entity is within distance */ private boolean isWithinDistance(Entity entity, Block block, int x, int y, int z) { Location loc = entity.getLocation(); return Math.abs(loc.getX() - block.getX()) <= x && Math.abs(loc.getY() - block.getY()) <= y && Math.abs(loc.getZ() - block.getZ()) <= z; } /** * Returns the head of a bed given one of its blocks. * @param block part of the bed * @return The head of the bed */ public static GlowBlock getHead(GlowBlock block) { MaterialData data = block.getState().getData(); if (!(data instanceof Bed)) { return null; } Bed bed = (Bed) data; if (bed.isHeadOfBed()) { return block; } else { return block.getRelative(bed.getFacing()); } } /** * Returns the foot of a bed given one of its blocks. * @param block part of the bed * @return The foot of the bed */ public static GlowBlock getFoot(GlowBlock block) { MaterialData data = block.getState().getData(); if (!(data instanceof Bed)) { return null; } Bed bed = (Bed) data; if (bed.isHeadOfBed()) { return block.getRelative(bed.getFacing().getOppositeFace()); } else { return block; } } /** * Returns whether a player can spawn within a block of specified material. * @param material the material * @return Whether spawning is possible */ public static boolean isValidSpawn(Material material) { switch (material) { case AIR: case SAPLING: case POWERED_RAIL: case DETECTOR_RAIL: case LONG_GRASS: case DEAD_BUSH: case YELLOW_FLOWER: case RED_ROSE: case BROWN_MUSHROOM: case RED_MUSHROOM: case TORCH: case REDSTONE_WIRE: case CROPS: case RAILS: case LEVER: case REDSTONE_TORCH_OFF: case REDSTONE_TORCH_ON: case STONE_BUTTON: case SNOW: case SUGAR_CANE_BLOCK: case DIODE_BLOCK_OFF: case DIODE_BLOCK_ON: case VINE: case TRIPWIRE_HOOK: case TRIPWIRE: case FLOWER_POT: case WOOD_BUTTON: case SKULL: case GOLD_PLATE: case IRON_PLATE: case REDSTONE_COMPARATOR_OFF: case REDSTONE_COMPARATOR_ON: case ACTIVATOR_RAIL: case CARPET: case DOUBLE_PLANT: return true; default: return false; } } /** * Returns an 'empty' block next to the bed used to put the player at when they exit a bed / respawn. * @param head head of the bed * @param foot foot of the bed * @return Exit block or {@code null} if all spots are blocked */ public static Block getExitLocation(GlowBlock head, GlowBlock foot) { // First check blocks near head for (int x = -1; x <= 1; x++) { for (int z = -1; z <= 1; z++) { Block b = head.getRelative(x, 0, z); boolean floorValid = b.getRelative(BlockFace.DOWN).getType().isSolid(); boolean bottomValid = isValidSpawn(b.getType()); boolean topValid = isValidSpawn(b.getRelative(BlockFace.UP).getType()); if (floorValid && bottomValid && topValid) { return b; } } } // Then check the last three blocks near foot BlockFace face = head.getFace(foot); int modX = face.getModX(); int modZ = face.getModZ(); for (int x = (modX == 0 ? -1 : modX); x <= (modX == 0 ? 1 : modX); x++) { for (int z = (modZ == 0 ? -1 : modZ); z <= (modZ == 0 ? 1 : modZ); z++) { Block b = foot.getRelative(x, 0, z); boolean floorValid = b.getRelative(BlockFace.DOWN).getType().isSolid(); boolean bottomValid = isValidSpawn(b.getType()); boolean topValid = isValidSpawn(b.getRelative(BlockFace.UP).getType()); if (floorValid && bottomValid && topValid) { return b; } } } return null; } }