Java tutorial
/** * BetonQuest - advanced quests for Bukkit * Copyright (C) 2016 Jakub "Co0sh" Sapalski * * 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 <http://www.gnu.org/licenses/>. */ package pl.betoncraft.betonquest.conversation; import java.util.ArrayList; import java.util.HashMap; import org.apache.commons.lang.StringUtils; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.event.inventory.InventoryCloseEvent; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.SkullMeta; import org.bukkit.scheduler.BukkitRunnable; import pl.betoncraft.betonquest.BetonQuest; import pl.betoncraft.betonquest.utils.PlayerConverter; /** * Inventory GUI for conversations * * @author Jakub Sapalski */ public class InventoryConvIO implements Listener, ConversationIO { private String response = null; private HashMap<Integer, String> options = new HashMap<>(); private int i = 0; private String npcName; private String npcNameColor; private String npcTextColor; private String numberFormat; private String optionColor; private String answerPrefix; private Conversation conv; private Player player; private Inventory inv; private boolean allowClose = false; private boolean switching = false; private Location loc; public InventoryConvIO(Conversation conv, String playerID) { this.conv = conv; this.player = PlayerConverter.getPlayer(playerID); HashMap<String, ChatColor[]> colors = ConversationColors.getColors(); StringBuilder string = new StringBuilder(); for (ChatColor color : colors.get("npc")) { string.append(color); } this.npcNameColor = string.toString(); string = new StringBuilder(); for (ChatColor color : colors.get("text")) { string.append(color); } this.npcTextColor = string.toString(); string = new StringBuilder(); for (ChatColor color : colors.get("number")) { string.append(color); } string.append("%number%."); this.numberFormat = string.toString(); string = new StringBuilder(); for (ChatColor color : colors.get("option")) { string.append(color); } this.optionColor = string.toString(); string = new StringBuilder(); for (ChatColor color : colors.get("player")) { string.append(color); } string.append(player.getName() + ChatColor.RESET + ": "); for (ChatColor color : colors.get("answer")) { string.append(color); } answerPrefix = string.toString(); loc = player.getLocation(); Bukkit.getPluginManager().registerEvents(this, BetonQuest.getInstance()); } @Override public void setNpcResponse(String npcName, String response) { this.npcName = npcName; this.response = response.replace('&', ''); } @Override public void addPlayerOption(String option) { i++; options.put(i, option.replace('&', '')); } @Override public void display() { // prevent displaying anything if the player closed the conversation // in the meantime if (conv.isEnded()) { return; } if (response == null) { end(); player.closeInventory(); return; } if (options.isEmpty()) { end(); } // each row contains 7 options, so get amount of rows int rows = (int) Math.floor(options.size() / 7); rows++; // this itemstack represents slots in the inventory inv = Bukkit.createInventory(null, 9 * rows, "NPC"); inv.setContents(new ItemStack[9 * rows]); ItemStack[] buttons = new ItemStack[9 * rows]; // set the NPC head ItemStack npc = new ItemStack(Material.SKULL_ITEM); npc.setDurability((short) 3); SkullMeta npcMeta = (SkullMeta) npc.getItemMeta(); npcMeta.setOwner(npcName); npcMeta.setDisplayName(npcNameColor + npcName); npcMeta.setLore(stringToLines(response, npcTextColor, null)); npc.setItemMeta(npcMeta); buttons[0] = npc; // this is the number of an option int next = 0; // now fill the slots for (int j = 0; j < 9 * rows; j++) { // skip first and second slots of each row if (j % 9 == 0 || j % 9 == 1) continue; // count option numbers, starting with 1 next++; // break if all options are set String option = options.get(next); if (option == null) { break; } // generate an itemstack for this option Material material = Material.ENDER_PEARL; short data = 0; // get the custom material if (option.matches("^\\{[a-zA-Z0-9_: ]+\\}.*$")) { String fullMaterial = option.substring(1, option.indexOf('}')); String materialName = fullMaterial; if (materialName.contains(":")) { int colonIndex = materialName.indexOf(':'); try { data = Short.valueOf(materialName.substring(colonIndex + 1)); } catch (NumberFormatException e) { data = 0; } materialName = materialName.substring(0, colonIndex); } Material m = Material.matchMaterial(materialName); option = option.replace("{" + fullMaterial + "}", ""); if (m == null) { material = Material.ENDER_PEARL; } else { material = m; } } // remove custom material prefix from the option options.put(next, option); // set the display name and lore of the option ItemStack item = new ItemStack(material); item.setDurability(data); ItemMeta meta = item.getItemMeta(); meta.setDisplayName(numberFormat.replace("%number%", Integer.toString(next))); ArrayList<String> lines = stringToLines(response, npcTextColor, npcNameColor + npcName + ChatColor.RESET + ": "); StringBuilder string = new StringBuilder(); for (ChatColor color : ConversationColors.getColors().get("number")) { string.append(color); } lines.addAll(stringToLines(option, optionColor, string.toString() + "- ")); meta.setLore(lines); item.setItemMeta(meta); buttons[j] = item; } player.sendMessage(npcNameColor + npcName + ChatColor.RESET + ": " + npcTextColor + response); inv.setContents(buttons); new BukkitRunnable() { @Override public void run() { switching = true; player.openInventory(inv); switching = false; } }.runTask(BetonQuest.getInstance()); } @EventHandler public void onInventoryClick(InventoryClickEvent event) { if (!(event.getWhoClicked() instanceof Player)) { return; } if (!((Player) event.getWhoClicked()).equals(player)) { return; } event.setCancelled(true); int slot = event.getRawSlot(); // calculate the option number if (slot % 9 > 1) { int row = (int) Math.floor(slot / 9); // raw column number minus two columns (npc head an empty space) // and plus one (because options are indexed starting with 1) int col = (slot % 9) - 2 + 1; // each row can have 7 options, add column number to get an option int choosen = (row * 7) + col; String message = options.get(choosen); if (message != null) { player.sendMessage(answerPrefix + message); conv.passPlayerAnswer(choosen); } } } @EventHandler public void onClose(InventoryCloseEvent event) { if (!(event.getPlayer() instanceof Player)) { return; } if (!((Player) event.getPlayer()).equals(player)) { return; } // allow for closing previous option inventory if (switching) { return; } // allow closing when the conversation has finished if (allowClose) { HandlerList.unregisterAll(this); return; } if (conv.isMovementBlock()) { new BukkitRunnable() { public void run() { player.teleport(loc); player.openInventory(inv); } }.runTask(BetonQuest.getInstance()); } else { conv.endConversation(); HandlerList.unregisterAll(this); } } @Override public void clear() { response = null; options.clear(); i = 0; } @Override public void end() { allowClose = true; if (response == null && options.isEmpty()) { player.closeInventory(); } } private ArrayList<String> stringToLines(String singleLine, String color, String prefix) { ArrayList<String> multiLine = new ArrayList<>(); boolean firstLinePrefix = prefix != null; if (prefix == null) prefix = ""; String[] lineBreaks = (prefix + singleLine).split("\n"); for (String brokenLine : lineBreaks) { String[] arr = brokenLine.split(" "); StringBuilder line = new StringBuilder(); for (int i = 0; i < arr.length; i++) { if (line.length() + arr[i].length() + 1 > 42) { if (firstLinePrefix) { firstLinePrefix = false; multiLine.add(StringUtils.replaceOnce(line.toString().trim(), prefix, prefix + color)); } else { multiLine.add(color + line.toString().trim()); } line = new StringBuilder(); } line.append(arr[i] + " "); } if (firstLinePrefix) { firstLinePrefix = false; multiLine.add(StringUtils.replaceOnce(line.toString().trim(), prefix, prefix + color)); } else { multiLine.add(color + line.toString().trim()); } } return multiLine; } }