From 397dd2c378593fed657e624ef41dc2efed89af0a Mon Sep 17 00:00:00 2001 From: Daniel Saukel Date: Sat, 11 Aug 2018 18:30:59 +0200 Subject: [PATCH] Dungeon items. Resolves #351 --- .../dungeonsxl/command/DCommandCache.java | 2 + .../command/DungeonItemCommand.java | 86 ++++++++++ .../erethon/dungeonsxl/config/DMessage.java | 8 + .../dungeonsxl/player/DGlobalPlayer.java | 11 ++ .../dungeonsxl/player/DPermission.java | 1 + .../de/erethon/dungeonsxl/util/NBTUtil.java | 149 ++++++++++++++++++ 6 files changed, 257 insertions(+) create mode 100644 src/main/java/de/erethon/dungeonsxl/command/DungeonItemCommand.java create mode 100644 src/main/java/de/erethon/dungeonsxl/util/NBTUtil.java diff --git a/src/main/java/de/erethon/dungeonsxl/command/DCommandCache.java b/src/main/java/de/erethon/dungeonsxl/command/DCommandCache.java index 5165c536..8808cd9b 100644 --- a/src/main/java/de/erethon/dungeonsxl/command/DCommandCache.java +++ b/src/main/java/de/erethon/dungeonsxl/command/DCommandCache.java @@ -37,6 +37,7 @@ public class DCommandCache extends DRECommandCache { public static EnterCommand ENTER = new EnterCommand(); public static EscapeCommand ESCAPE = new EscapeCommand(); public static DeleteCommand DELETE = new DeleteCommand(); + public static DungeonItemCommand DUNGEON_ITEM = new DungeonItemCommand(); public static GameCommand GAME = new GameCommand(); public static GroupCommand GROUP = new GroupCommand(); public static HelpCommand HELP = new HelpCommand(); @@ -64,6 +65,7 @@ public class DCommandCache extends DRECommandCache { BREAK, CREATE, DELETE, + DUNGEON_ITEM, EDIT, ENTER, ESCAPE, diff --git a/src/main/java/de/erethon/dungeonsxl/command/DungeonItemCommand.java b/src/main/java/de/erethon/dungeonsxl/command/DungeonItemCommand.java new file mode 100644 index 00000000..d0135ab7 --- /dev/null +++ b/src/main/java/de/erethon/dungeonsxl/command/DungeonItemCommand.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2012-2018 Frank Baumann + * + * 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 de.erethon.dungeonsxl.command; + +import de.erethon.commons.chat.MessageUtil; +import de.erethon.commons.command.DRECommand; +import de.erethon.dungeonsxl.config.DMessage; +import de.erethon.dungeonsxl.player.DPermission; +import de.erethon.dungeonsxl.util.NBTUtil; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.PlayerInventory; + +/** + * @author Frank Baumann, Daniel Saukel + */ +public class DungeonItemCommand extends DRECommand { + + public DungeonItemCommand() { + setCommand("dungeonItem"); + setAliases("di"); + setMinArgs(0); + setMaxArgs(1); + setHelp(DMessage.HELP_CMD_DUNGEON_ITEM.getMessage()); + setPermission(DPermission.DUNGEON_ITEM.getNode()); + setPlayerCommand(true); + setConsoleCommand(false); + } + + @Override + public void onExecute(String[] args, CommandSender sender) { + Player player = (Player) sender; + PlayerInventory inv = player.getInventory(); + + ItemStack bukkitStack = inv.getItemInHand(); + if (bukkitStack == null) { + MessageUtil.sendTitleMessage(player, DMessage.ERROR_NO_ITEM_IN_MAIN_HAND.getMessage()); + return; + } + Object tag = NBTUtil.getTag(bukkitStack); + + String action = args.length >= 2 ? args[1] : "info"; + if (action.equalsIgnoreCase("true")) { + if (tag == null) { + tag = NBTUtil.createTag(); + } + NBTUtil.addBoolean(tag, NBTUtil.DUNGEON_ITEM_KEY, true); + inv.setItemInHand(NBTUtil.setTag(bukkitStack, tag)); + MessageUtil.sendMessage(sender, DMessage.CMD_DUNGEON_ITEM_SET_DUNGEON.getMessage()); + MessageUtil.sendMessage(sender, DMessage.HELP_DUNGEON_ITEM.getMessage()); + + } else if (action.equalsIgnoreCase("false")) { + if (tag != null) { + NBTUtil.removeKey(tag, NBTUtil.DUNGEON_ITEM_KEY); + inv.setItemInHand(NBTUtil.setTag(bukkitStack, tag)); + } + MessageUtil.sendMessage(sender, DMessage.CMD_DUNGEON_ITEM_INFO_GLOBAL.getMessage()); + MessageUtil.sendMessage(sender, DMessage.HELP_GLOBAL_ITEM.getMessage()); + + } else { + if (tag != null && NBTUtil.hasKey(tag, NBTUtil.DUNGEON_ITEM_KEY)) { + MessageUtil.sendMessage(sender, DMessage.CMD_DUNGEON_ITEM_INFO_DUNGEON.getMessage()); + MessageUtil.sendMessage(sender, DMessage.HELP_DUNGEON_ITEM.getMessage()); + } else { + MessageUtil.sendMessage(sender, DMessage.CMD_DUNGEON_ITEM_INFO_GLOBAL.getMessage()); + MessageUtil.sendMessage(sender, DMessage.HELP_GLOBAL_ITEM.getMessage()); + } + } + } + +} diff --git a/src/main/java/de/erethon/dungeonsxl/config/DMessage.java b/src/main/java/de/erethon/dungeonsxl/config/DMessage.java index 4263932c..e86fda4c 100644 --- a/src/main/java/de/erethon/dungeonsxl/config/DMessage.java +++ b/src/main/java/de/erethon/dungeonsxl/config/DMessage.java @@ -37,6 +37,10 @@ public enum DMessage implements Message { CMD_CHATSPY_START("Cmd_Chatspy_Start", "&6You started to spy the dungeon chat."), CMD_DELETE_BACKUPS("Cmd_Delete_Backups", "&6Do you wish to delete all saved backups as well?"), CMD_DELETE_SUCCESS("Cmd_Delete_Success", "&6Successfully deleted the map &4&v1&6."), + CMD_DUNGEON_ITEM_INFO_DUNGEON("Cmd_DungeonItem_Info_Dungeon", "&6This item is a &4dungeon item&6."), + CMD_DUNGEON_ITEM_INFO_GLOBAL("Cmd_DungeonItem_Info_Global", "&6This item is a &4global item&6."), + CMD_DUNGEON_ITEM_SET_DUNGEON("Cmd_DungeonItem_Set_Dungeon", "&6Successfully made this item a &4dungeon item&6."), + CMD_DUNGEON_ITEM_SET_GLOBAL("Cmd_DungeonItem_Set_Global", "&6Successfully made this item a &4global item&6."), CMD_ENTER_SUCCESS("Cmd_Enter", "&6The group &4&v1 &6successfully entered the game of the group &4&v2&6."), CMD_IMPORT_SUCCESS("Cmd_Import", "&6Successfully imported the world &4&v1&6."), CMD_INVITE_SUCCESS("Cmd_Invite_Success", "&6Player &4&v1&6 was successfully invited to edit the map &4&v2&6."), @@ -77,6 +81,7 @@ public enum DMessage implements Message { ERROR_NAME_TO_LONG("Error_NameToLong", "&4The name may not be longer than 15 characters!"), ERROR_NO_CONSOLE_COMMAND("Error_NoConsoleCommand", "&6/dxl &v1&4 cannot be executed as console!"), ERROR_NO_GAME("Error_NoGame", "&4You currently do not take part in a game."), + ERROR_NO_ITEM_IN_MAIN_HAND("Error_NoItemInMainHand", "&4You do not have an item in your main hand."), ERROR_NO_LEAVE_IN_TUTORIAL("Error_NoLeaveInTutorial", "&4You cannot use this command in the tutorial!"), ERROR_NO_PERMISSIONS("Error_NoPermissions", "&4You do not have permission to do this!"), ERROR_NO_PLAYER_COMMAND("Error_NoPlayerCommand", "&6/dxl &v1&4 cannot be executed as player!"), @@ -105,6 +110,7 @@ public enum DMessage implements Message { HELP_CMD_CHATSPY("Help_Cmd_Chatspy", "/dxl chatspy - Dis/enables the spymode"), HELP_CMD_CREATE("Help_Cmd_Create", "/dxl create [name] - Creates a new dungeon map"), HELP_CMD_DELETE("Help_Cmd_Delete", "/dxl delete [name] - Deletes a dungeon map"), + HELP_CMD_DUNGEON_ITEM("Help_Cmd_DungeonItem", "/dxl dungeonItem [true|false|info] - Sets the item stack in the player's hand to be one that cannot be taken out of a dungeon"), HELP_CMD_EDIT("Help_Cmd_Edit", "/dxl edit [map] - Edit an existing dungeon map"), HELP_CMD_ENTER("Help_Cmd_Enter", "/dxl enter ([joining group]) [target group] - Let the joining group enter the game of the target group"), HELP_CMD_ESCAPE("Help_Cmd_Escape", "/dxl escape - Leaves the current edit world without saving"), @@ -138,6 +144,8 @@ public enum DMessage implements Message { HELP_CMD_SETTINGS("Help_Cmd_Settings", "/dxl settings ([edit|global|player])- Opens the settings menu"), HELP_CMD_TEST("Help_Cmd_Test", "/dxl test - Starts the game in test mode"), HELP_CMD_UNINVITE("Help_Cmd_Uninvite", "/dxl uninvite [player] [dungeon] - Uninvite a player to edit a dungeon"), + HELP_DUNGEON_ITEM("Help_DungeonItem", "&6After finishing a game, &4dungeon items &6are removed from the player's inventory even if the respective dungeon setup in general allows to keep it."), + HELP_GLOBAL_ITEM("Help_GlobalItem", "&6After finishing a game, &4global items &6can be taken out of dungeons if the respective dungeon setup in general allows to keep the inventory."), GROUP_BED_DESTROYED("Group_BedDestroyed", "&6The bed of the group &4&v1 &6has been destroyed by &4&v2&6!"), GROUP_CONGRATS("Group_Congrats", "&6Congratulations!"), GROUP_CONGRATS_SUB("Group_CongratsSub", "&l&4Your group &v1 &4won the match!"), diff --git a/src/main/java/de/erethon/dungeonsxl/player/DGlobalPlayer.java b/src/main/java/de/erethon/dungeonsxl/player/DGlobalPlayer.java index b1526665..7e685897 100644 --- a/src/main/java/de/erethon/dungeonsxl/player/DGlobalPlayer.java +++ b/src/main/java/de/erethon/dungeonsxl/player/DGlobalPlayer.java @@ -26,6 +26,7 @@ import de.erethon.dungeonsxl.dungeon.Dungeon; import de.erethon.dungeonsxl.event.dgroup.DGroupCreateEvent; import de.erethon.dungeonsxl.game.Game; import de.erethon.dungeonsxl.global.DPortal; +import de.erethon.dungeonsxl.util.NBTUtil; import de.erethon.dungeonsxl.world.DGameWorld; import java.io.File; import java.util.List; @@ -355,6 +356,16 @@ public class DGlobalPlayer implements PlayerWrapper { } player.addPotionEffects(data.getOldPotionEffects()); + + } else { + for (ItemStack item : player.getInventory().getStorageContents()) { + if (item == null) { + continue; + } + if (NBTUtil.isDungeonItem(item)) { + item.setAmount(0); + } + } } } catch (NullPointerException exception) { diff --git a/src/main/java/de/erethon/dungeonsxl/player/DPermission.java b/src/main/java/de/erethon/dungeonsxl/player/DPermission.java index 46b48bd1..d3d344c3 100644 --- a/src/main/java/de/erethon/dungeonsxl/player/DPermission.java +++ b/src/main/java/de/erethon/dungeonsxl/player/DPermission.java @@ -39,6 +39,7 @@ public enum DPermission { CMD_EDIT("cmdedit", OP), CREATE("create", OP), DELETE("delete", OP), + DUNGEON_ITEM("dungeonitem", OP), EDIT("edit", OP), ENTER("enter", OP), ESCAPE("escape", TRUE), diff --git a/src/main/java/de/erethon/dungeonsxl/util/NBTUtil.java b/src/main/java/de/erethon/dungeonsxl/util/NBTUtil.java new file mode 100644 index 00000000..52dc7ba9 --- /dev/null +++ b/src/main/java/de/erethon/dungeonsxl/util/NBTUtil.java @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2012-2018 Frank Baumann + * + * 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 de.erethon.dungeonsxl.util; + +import static de.erethon.commons.misc.ReflectionUtil.*; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import org.bukkit.inventory.ItemStack; + +/** + * A minimalistic NMS ItemStack NBT util. + * + * @author Daniel Saukel + */ +public class NBTUtil { + + public static final String DUNGEON_ITEM_KEY = "DungeonItem"; + + private static Method HAS_KEY; + private static Method REMOVE; + private static Method SET_BOOLEAN; + + static { + try { + HAS_KEY = NBT_TAG_COMPOUND.getDeclaredMethod("hasKey", String.class); + REMOVE = NBT_TAG_COMPOUND.getDeclaredMethod("remove", String.class); + SET_BOOLEAN = NBT_TAG_COMPOUND.getDeclaredMethod("setBoolean", String.class, boolean.class); + } catch (NoSuchMethodException | SecurityException exception) { + exception.printStackTrace(); + } + } + + /** + * Returns the NBT data of an ItemStack + * + * @param item the Bukkit representation of the ItemStack + * @return the NBT data of the ItemStack + */ + public static Object getTag(ItemStack item) { + try { + return ITEM_STACK_GET_TAG.invoke(CRAFT_ITEM_STACK_AS_NMS_COPY.invoke(null, item)); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException exception) { + exception.printStackTrace(); + return null; + } + } + + /** + * Returns a new NBTTagCompound + * + * @return a new NBTTagCompound + */ + public static Object createTag() { + try { + return NBT_TAG_COMPOUND.newInstance(); + } catch (IllegalAccessException | InstantiationException exception) { + exception.printStackTrace(); + return null; + } + } + + /** + * Returns a copy of the ItemStack with the applied NBT data + * + * @param item the Bukkit representation of the ItemStack + * @param tag the NBT data to set + * @return a new copy of the Bukkit ItemStack with the applied changes + */ + public static ItemStack setTag(ItemStack item, Object tag) { + try { + Object nmsStack = CRAFT_ITEM_STACK_AS_NMS_COPY.invoke(null, item); + ITEM_STACK_SET_TAG.invoke(nmsStack, tag); + return (ItemStack) CRAFT_ITEM_STACK_AS_BUKKIT_COPY.invoke(null, nmsStack); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException exception) { + exception.printStackTrace(); + return null; + } + } + + /** + * Returns if the NBTTagCompound contains the key + * + * @param tag the NBTTagCompound + * @param key a key + * @return if the NBTTagCompound contains the key + */ + public static boolean hasKey(Object tag, String key) { + try { + return (boolean) HAS_KEY.invoke(tag, key); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException exception) { + exception.printStackTrace(); + return false; + } + } + + /** + * Adds the key and its value to the NBTTagCompound + * + * @param tag the NBTTagCompound + * @param key the key to add + * @param value the value to add + */ + public static void addBoolean(Object tag, String key, boolean value) { + try { + SET_BOOLEAN.invoke(tag, key, value); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException exception) { + exception.printStackTrace(); + } + } + + /** + * Removes the key from the NBTTagCompound + * + * @param tag the NBTTagCompound + * @param key the key to remove + */ + public static void removeKey(Object tag, String key) { + try { + REMOVE.invoke(tag, key); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException exception) { + exception.printStackTrace(); + } + } + + /** + * Returns if the item is a dungeon item + * + * @param item a Bukkit ItemStack + * @return if the item is a dungeon item + */ + public static boolean isDungeonItem(ItemStack item) { + return getTag(item) != null && hasKey(getTag(item), DUNGEON_ITEM_KEY); + } + +}