diff --git a/src/main/java/com/sk89q/worldedit/LocalWorld.java b/src/main/java/com/sk89q/worldedit/LocalWorld.java index 226256f06..fa7d3e539 100644 --- a/src/main/java/com/sk89q/worldedit/LocalWorld.java +++ b/src/main/java/com/sk89q/worldedit/LocalWorld.java @@ -32,6 +32,7 @@ import com.sk89q.worldedit.blocks.FurnaceBlock; import com.sk89q.worldedit.blocks.MobSpawnerBlock; import com.sk89q.worldedit.blocks.NoteBlock; import com.sk89q.worldedit.blocks.SignBlock; +import com.sk89q.worldedit.blocks.SkullBlock; import com.sk89q.worldedit.foundation.Block; import com.sk89q.worldedit.foundation.World; import com.sk89q.worldedit.regions.Region; @@ -591,6 +592,12 @@ public abstract class LocalWorld implements World { return block; } + case BlockID.HEAD: { + SkullBlock block = new SkullBlock(data); + copyFromWorld(pt, block); + return block; + } + default: return new BaseBlock(type, data); } diff --git a/src/main/java/com/sk89q/worldedit/WorldEdit.java b/src/main/java/com/sk89q/worldedit/WorldEdit.java index 93c5688a1..617618100 100644 --- a/src/main/java/com/sk89q/worldedit/WorldEdit.java +++ b/src/main/java/com/sk89q/worldedit/WorldEdit.java @@ -59,6 +59,7 @@ import com.sk89q.worldedit.blocks.ItemType; import com.sk89q.worldedit.blocks.MobSpawnerBlock; import com.sk89q.worldedit.blocks.NoteBlock; import com.sk89q.worldedit.blocks.SignBlock; +import com.sk89q.worldedit.blocks.SkullBlock; import com.sk89q.worldedit.commands.BiomeCommands; import com.sk89q.worldedit.commands.ChunkCommands; import com.sk89q.worldedit.commands.ClipboardCommands; @@ -540,6 +541,43 @@ public class WorldEdit { return new NoteBlock(data, (byte) 0); } + case HEAD: + // allow setting type/player/rotation + if (blockAndExtraData.length > 1) { + // and thus, the format shall be "|type|rotation" or "|type" or "|rotation" + byte rot = 0; + String type = ""; + try { + rot = Byte.parseByte(blockAndExtraData[1]); + } catch (NumberFormatException e) { + type = blockAndExtraData[1]; + if (blockAndExtraData.length > 2) { + try { + rot = Byte.parseByte(blockAndExtraData[2]); + } catch (NumberFormatException e2) { + throw new InvalidItemException(arg, "Second part of skull metadata should be a number."); + } + } + } + byte skullType = 0; + // type is either the mob type or the player name + // sorry for the four minecraft accounts named "skeleton", "wither", "zombie", or "creeper" + if (!type.isEmpty()) { + if (type.equalsIgnoreCase("skeleton")) skullType = 0; + else if (type.equalsIgnoreCase("wither")) skullType = 1; + else if (type.equalsIgnoreCase("zombie")) skullType = 2; + else if (type.equalsIgnoreCase("creeper")) skullType = 4; + else skullType = 3; + } + if (skullType == 3) { + return new SkullBlock(data, rot, type); + } else { + return new SkullBlock(data, skullType, rot); + } + } else { + return new SkullBlock(data); + } + default: return new BaseBlock(blockId, data); } diff --git a/src/main/java/com/sk89q/worldedit/blocks/SkullBlock.java b/src/main/java/com/sk89q/worldedit/blocks/SkullBlock.java new file mode 100644 index 000000000..a0e206127 --- /dev/null +++ b/src/main/java/com/sk89q/worldedit/blocks/SkullBlock.java @@ -0,0 +1,196 @@ +// $Id$ +/* + * WorldEdit + * Copyright (C) 2010 sk89q and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.blocks; + +import java.util.HashMap; +import java.util.Map; + +import com.sk89q.jnbt.ByteTag; +import com.sk89q.jnbt.CompoundTag; +import com.sk89q.jnbt.StringTag; +import com.sk89q.jnbt.Tag; +import com.sk89q.worldedit.data.DataException; + +/** + * A skull block. + */ +public class SkullBlock extends BaseBlock implements TileEntityBlock { + + private String owner = ""; // notchian + private byte skullType; // stored here for block, in damage value for item + private byte rot; // only matters if block data == 0x1 (on floor) + + /** + * Construct the skull block with a default type of skelton. + * @param data data value to set, controls placement + */ + public SkullBlock(int data) { + this(data, (byte) 0); + } + + /** + * Construct the skull block with a given type. + * 0 - skeleton + * 1 - wither skelly + * 2 - zombie + * 3 - human + * 4 - creeper + * @param data data value to set, controls placement + * @param type type of skull + */ + public SkullBlock(int data, byte type) { + this(data, type, (byte) 0); + } + + /** + * Construct the skull block with a given type and rotation. + * @param data data value to set, controls placement + * @param type type of skull + * @param rot rotation (if on floor) + */ + public SkullBlock(int data, byte type, byte rot) { + super(BlockID.HEAD, 1); + if (type < (byte) 0 || type > (byte) 4) { + this.skullType = (byte) 0; + } else { + this.skullType = type; + } + this.rot = rot; + this.owner = ""; + } + + /** + * Construct the skull block with a given rotation and owner. + * The type is assumed to be player unless owner is null or empty. + * @param data data value to set, controls placement + * @param rot rotation of skull + * @param owner name of player + */ + public SkullBlock(int data, byte rot, String owner) { + super(BlockID.HEAD, 1); + this.rot = rot; + this.setOwner(owner); + if (owner == null || owner.isEmpty()) this.skullType = (byte) 0; + } + + /** + * Set the skull's owner. Automatically sets type to player if not empty or null. + * @param owner player name to set the skull to + */ + public void setOwner(String owner) { + if (owner == null) { + this.owner = ""; + } else { + if (owner.length() > 16 || owner.isEmpty()) this.owner = ""; + else this.owner = owner; + } + if (this.owner != null && !this.owner.isEmpty()) this.skullType = (byte) 3; + } + + /** + * Get the skull's owner. Returns null if unset. + * @return player name or null + */ + public String getOwner() { + return owner; + } + + /** + * Get the type of skull. + * @return the skullType + */ + public byte getSkullType() { + return skullType; + } + + /** + * Set the type of skull; + * @param skullType the skullType to set + */ + public void setSkullType(byte skullType) { + this.skullType = skullType; + } + + /** + * Get rotation of skull. This only means anything if the block data is 1. + * @return the rotation + */ + public byte getRot() { + return rot; + } + + /** + * Set the rotation of skull. + * @param rot the rotation to set + */ + public void setRot(byte rot) { + this.rot = rot; + } + + @Override + public boolean hasNbtData() { + return true; + } + + @Override + public String getNbtId() { + return "Skull"; + } + + @Override + public CompoundTag getNbtData() { + Map values = new HashMap(); + values.put("SkullType", new ByteTag("SkullType", skullType)); + if (owner == null) owner = ""; + values.put("ExtraType", new StringTag("ExtraType", owner)); + values.put("Rot", new ByteTag("Rot", rot)); + return new CompoundTag(getNbtId(), values); + } + + @Override + public void setNbtData(CompoundTag rootTag) throws DataException { + if (rootTag == null) { + return; + } + + Map values = rootTag.getValue(); + + Tag t; + + t = values.get("id"); + if (!(t instanceof StringTag) + || !((StringTag) t).getValue().equals("Skull")) { + throw new DataException("'Skull' tile entity expected"); + } + + t = values.get("SkullType"); + if (t instanceof ByteTag) { + skullType = ((ByteTag) t).getValue(); + } + t = values.get("ExtraType"); + if (t != null && t instanceof StringTag) { + owner = ((StringTag) t).getValue(); + } + t = values.get("Rot"); + if (t instanceof ByteTag) { + rot = ((ByteTag) t).getValue(); + } + } +} diff --git a/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java b/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java index b4b73bf6c..00c1243b8 100644 --- a/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java +++ b/src/main/java/com/sk89q/worldedit/bukkit/BukkitWorld.java @@ -33,15 +33,18 @@ import java.util.logging.Logger; import org.bukkit.Effect; import org.bukkit.Location; import org.bukkit.Material; +import org.bukkit.SkullType; import org.bukkit.TreeType; import org.bukkit.World; import org.bukkit.block.Biome; import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; import org.bukkit.block.BlockState; import org.bukkit.block.Chest; import org.bukkit.block.CreatureSpawner; import org.bukkit.block.Furnace; import org.bukkit.block.Sign; +import org.bukkit.block.Skull; import org.bukkit.enchantments.Enchantment; import org.bukkit.entity.Ambient; import org.bukkit.entity.Animals; @@ -82,6 +85,7 @@ import com.sk89q.worldedit.blocks.FurnaceBlock; import com.sk89q.worldedit.blocks.MobSpawnerBlock; import com.sk89q.worldedit.blocks.NoteBlock; import com.sk89q.worldedit.blocks.SignBlock; +import com.sk89q.worldedit.blocks.SkullBlock; import com.sk89q.worldedit.bukkit.entity.BukkitEntity; import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.util.TreeGenerator; @@ -360,6 +364,95 @@ public class BukkitWorld extends LocalWorld { return true; } + if (block instanceof SkullBlock) { + // Skull block + Block bukkitBlock = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()); + if (bukkitBlock == null) return false; + BlockState state = bukkitBlock.getState(); + if (!(state instanceof org.bukkit.block.Skull)) return false; + Skull bukkit = (Skull) state; + SkullBlock we = (SkullBlock) block; + // this is dumb + SkullType skullType = SkullType.SKELETON; + switch (we.getSkullType()) { + case 0: + skullType = SkullType.SKELETON; + break; + case 1: + skullType = SkullType.WITHER; + break; + case 2: + skullType = SkullType.ZOMBIE; + break; + case 3: + skullType = SkullType.PLAYER; + break; + case 4: + skullType = SkullType.CREEPER; + break; + } + bukkit.setSkullType(skullType); + BlockFace rotation; + switch (we.getRot()) { + // soooo dumb + case 0: + rotation = BlockFace.NORTH; + break; + case 1: + rotation = BlockFace.NORTH_NORTH_EAST; + break; + case 2: + rotation = BlockFace.NORTH_EAST; + break; + case 3: + rotation = BlockFace.EAST_NORTH_EAST; + break; + case 4: + rotation = BlockFace.EAST; + break; + case 5: + rotation = BlockFace.EAST_SOUTH_EAST; + break; + case 6: + rotation = BlockFace.SOUTH_EAST; + break; + case 7: + rotation = BlockFace.SOUTH_SOUTH_EAST; + break; + case 8: + rotation = BlockFace.SOUTH; + break; + case 9: + rotation = BlockFace.SOUTH_SOUTH_WEST; + break; + case 10: + rotation = BlockFace.SOUTH_WEST; + break; + case 11: + rotation = BlockFace.WEST_SOUTH_WEST; + break; + case 12: + rotation = BlockFace.WEST; + break; + case 13: + rotation = BlockFace.WEST_NORTH_WEST; + break; + case 14: + rotation = BlockFace.NORTH_WEST; + break; + case 15: + rotation = BlockFace.NORTH_NORTH_WEST; + break; + default: + rotation = BlockFace.NORTH; + break; + } + bukkit.setRotation(rotation); + if (we.getOwner() != null && !we.getOwner().isEmpty()) bukkit.setOwner(we.getOwner()); + bukkit.update(true); + return true; + } + if (!skipNmsAccess) { try { return NmsBlock.set(world, pt, block); @@ -432,6 +525,91 @@ public class BukkitWorld extends LocalWorld { return true; } + if (block instanceof SkullBlock) { + // Skull block + Block bukkitBlock = world.getBlockAt(pt.getBlockX(), pt.getBlockY(), pt.getBlockZ()); + if (bukkitBlock == null) return false; + BlockState state = bukkitBlock.getState(); + if (!(state instanceof org.bukkit.block.Skull)) return false; + Skull bukkit = (Skull) state; + SkullBlock we = (SkullBlock) block; + byte skullType = 0; + switch (bukkit.getSkullType()) { + // this is dumb but whoever wrote the class is stupid + case SKELETON: + skullType = 0; + break; + case WITHER: + skullType = 1; + break; + case ZOMBIE: + skullType = 2; + break; + case PLAYER: + skullType = 3; + break; + case CREEPER: + skullType = 4; + break; + } + we.setSkullType(skullType); + byte rot = 0; + switch (bukkit.getRotation()) { + // this is even more dumb, hurray for copy/paste + case NORTH: + rot = (byte) 0; + break; + case NORTH_NORTH_EAST: + rot = (byte) 1; + break; + case NORTH_EAST: + rot = (byte) 2; + break; + case EAST_NORTH_EAST: + rot = (byte) 3; + break; + case EAST: + rot = (byte) 4; + break; + case EAST_SOUTH_EAST: + rot = (byte) 5; + break; + case SOUTH_EAST: + rot = (byte) 6; + break; + case SOUTH_SOUTH_EAST: + rot = (byte) 7; + break; + case SOUTH: + rot = (byte) 8; + break; + case SOUTH_SOUTH_WEST: + rot = (byte) 9; + break; + case SOUTH_WEST: + rot = (byte) 10; + break; + case WEST_SOUTH_WEST: + rot = (byte) 11; + break; + case WEST: + rot = (byte) 12; + break; + case WEST_NORTH_WEST: + rot = (byte) 13; + break; + case NORTH_WEST: + rot = (byte) 14; + break; + case NORTH_NORTH_WEST: + rot = (byte) 15; + break; + } + we.setRot(rot); + we.setOwner(bukkit.hasOwner() ? bukkit.getOwner() : ""); + return true; + } + return false; } @@ -997,6 +1175,7 @@ public class BukkitWorld extends LocalWorld { //case BlockID.DISPENSER: //case BlockID.MOB_SPAWNER: case BlockID.NOTE_BLOCK: + case BlockID.HEAD: return super.getBlock(pt); default: if (!skipNmsAccess) {