diff --git a/src/main/java/de/epiceric/shopchest/ShopChest.java b/src/main/java/de/epiceric/shopchest/ShopChest.java index d7cf84b..6bf4c19 100644 --- a/src/main/java/de/epiceric/shopchest/ShopChest.java +++ b/src/main/java/de/epiceric/shopchest/ShopChest.java @@ -18,6 +18,7 @@ import de.epiceric.shopchest.language.LanguageUtils; import de.epiceric.shopchest.listeners.AreaShopListener; import de.epiceric.shopchest.listeners.BlockExplodeListener; import de.epiceric.shopchest.listeners.ChestProtectListener; +import de.epiceric.shopchest.listeners.CreativeModeListener; import de.epiceric.shopchest.listeners.NotifyPlayerOnJoinListener; import de.epiceric.shopchest.listeners.ShopInteractListener; import de.epiceric.shopchest.listeners.ShopItemListener; @@ -29,6 +30,7 @@ import de.epiceric.shopchest.sql.Database; import de.epiceric.shopchest.sql.MySQL; import de.epiceric.shopchest.sql.SQLite; import de.epiceric.shopchest.utils.Callback; +import de.epiceric.shopchest.utils.ClickType; import de.epiceric.shopchest.utils.Permissions; import de.epiceric.shopchest.utils.ShopUpdater; import de.epiceric.shopchest.utils.ShopUtils; @@ -213,6 +215,8 @@ public class ShopChest extends JavaPlugin { public void onDisable() { debug("Disabling ShopChest..."); + ClickType.clear(); + if (updater != null) { debug("Stopping updater"); updater.stop(); @@ -386,6 +390,7 @@ public class ShopChest extends JavaPlugin { getServer().getPluginManager().registerEvents(new ShopInteractListener(this), this); getServer().getPluginManager().registerEvents(new NotifyPlayerOnJoinListener(this), this); getServer().getPluginManager().registerEvents(new ChestProtectListener(this), this); + getServer().getPluginManager().registerEvents(new CreativeModeListener(this), this); if (!Utils.getServerVersion().equals("v1_8_R1")) { getServer().getPluginManager().registerEvents(new BlockExplodeListener(this), this); diff --git a/src/main/java/de/epiceric/shopchest/command/ShopCommand.java b/src/main/java/de/epiceric/shopchest/command/ShopCommand.java index 83119cf..619c112 100644 --- a/src/main/java/de/epiceric/shopchest/command/ShopCommand.java +++ b/src/main/java/de/epiceric/shopchest/command/ShopCommand.java @@ -7,6 +7,8 @@ import de.epiceric.shopchest.language.LanguageUtils; import de.epiceric.shopchest.language.Message; import de.epiceric.shopchest.language.Replacement; import de.epiceric.shopchest.utils.Permissions; +import de.epiceric.shopchest.utils.ClickType.SelectClickType; + import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.command.*; @@ -27,6 +29,7 @@ public class ShopCommand { private ShopChest plugin; private String name; private PluginCommand pluginCommand; + private ShopCommandExecutor executor; private List subCommands = new ArrayList<>(); @@ -40,8 +43,8 @@ public class ShopCommand { this.plugin = plugin; this.name = Config.mainCommandName; this.pluginCommand = createPluginCommand(); + this.executor = new ShopCommandExecutor(plugin); - ShopCommandExecutor executor = new ShopCommandExecutor(plugin); ShopTabCompleter tabCompleter = new ShopTabCompleter(plugin); final Replacement cmdReplacement = new Replacement(Placeholder.COMMAND, name); @@ -150,6 +153,14 @@ public class ShopCommand { return pluginCommand; } + /** + * Call the second part of the create method after the player + * has selected an item from the creative inventory. + */ + public void createShopAfterSelected(Player player, SelectClickType clickType) { + executor.create2(player, clickType); + } + private PluginCommand createPluginCommand() { plugin.debug("Creating plugin command"); try { diff --git a/src/main/java/de/epiceric/shopchest/command/ShopCommandExecutor.java b/src/main/java/de/epiceric/shopchest/command/ShopCommandExecutor.java index d99ac48..b455dd7 100644 --- a/src/main/java/de/epiceric/shopchest/command/ShopCommandExecutor.java +++ b/src/main/java/de/epiceric/shopchest/command/ShopCommandExecutor.java @@ -14,6 +14,7 @@ import de.epiceric.shopchest.language.Message; import de.epiceric.shopchest.language.Replacement; import de.epiceric.shopchest.shop.Shop; import de.epiceric.shopchest.shop.ShopProduct; +import de.epiceric.shopchest.shop.Shop.ShopType; import de.epiceric.shopchest.utils.Callback; import de.epiceric.shopchest.utils.ClickType; import de.epiceric.shopchest.utils.ItemUtils; @@ -21,7 +22,11 @@ import de.epiceric.shopchest.utils.Permissions; import de.epiceric.shopchest.utils.ShopUtils; import de.epiceric.shopchest.utils.UpdateChecker; import de.epiceric.shopchest.utils.Utils; +import de.epiceric.shopchest.utils.ClickType.CreateClickType; +import de.epiceric.shopchest.utils.ClickType.SelectClickType; + import org.bukkit.Bukkit; +import org.bukkit.GameMode; import org.bukkit.OfflinePlayer; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; @@ -262,22 +267,47 @@ class ShopCommandExecutor implements CommandExecutor { // Check if item in hand if (inHand == null) { - p.sendMessage(LanguageUtils.getMessage(Message.NO_ITEM_IN_HAND)); plugin.debug(p.getName() + " does not have an item in his hand"); - return; + + if (!Config.creativeSelectItem) { + p.sendMessage(LanguageUtils.getMessage(Message.NO_ITEM_IN_HAND)); + return; + } + + ClickType.setPlayerClickType(p, new SelectClickType(p.getGameMode(), amount, buyPrice, sellPrice, shopType)); + p.setGameMode(GameMode.CREATIVE); + + p.sendMessage(LanguageUtils.getMessage(Message.SELECT_ITEM)); + } else { + SelectClickType ct = new SelectClickType(null, amount, buyPrice, sellPrice, shopType); + ct.setItem(inHand); + create2(p, ct); } + } + + /** + * SHALL ONLY BE CALLED VIA {@link ShopCommand#createShopAfterSelected()} + */ + protected void create2(Player p, SelectClickType selectClickType) { + ItemStack itemStack = selectClickType.getItem(); + int amount = selectClickType.getAmount(); + double buyPrice = selectClickType.getBuyPrice(); + double sellPrice = selectClickType.getSellPrice(); + boolean buyEnabled = buyPrice > 0; + boolean sellEnabled = sellPrice > 0; + ShopType shopType = selectClickType.getShopType(); // Check if item on blacklist for (String item :Config.blacklist) { - ItemStack itemStack = ItemUtils.getItemStack(item); + ItemStack is = ItemUtils.getItemStack(item); - if (itemStack == null) { + if (is == null) { plugin.getLogger().warning("Invalid item found in blacklist: " + item); plugin.debug("Invalid item in blacklist: " + item); continue; } - if (itemStack.getType().equals(inHand.getType()) && itemStack.getDurability() == inHand.getDurability()) { + if (is.getType().equals(itemStack.getType()) && is.getDurability() == itemStack.getDurability()) { p.sendMessage(LanguageUtils.getMessage(Message.CANNOT_SELL_ITEM)); plugin.debug(p.getName() + "'s item is on the blacklist"); return; @@ -286,16 +316,16 @@ class ShopCommandExecutor implements CommandExecutor { // Check if prices lower than minimum price for (String key :Config.minimumPrices) { - ItemStack itemStack = ItemUtils.getItemStack(key); + ItemStack is = ItemUtils.getItemStack(key); double minPrice = plugin.getConfig().getDouble("minimum-prices." + key); - if (itemStack == null) { + if (is == null) { plugin.getLogger().warning("Invalid item found in minimum-prices: " + key); plugin.debug("Invalid item in minimum-prices: " + key); continue; } - if (itemStack.getType().equals(inHand.getType()) && itemStack.getDurability() == inHand.getDurability()) { + if (is.getType().equals(itemStack.getType()) && is.getDurability() == itemStack.getDurability()) { if (buyEnabled) { if ((buyPrice < amount * minPrice) && (buyPrice > 0)) { p.sendMessage(LanguageUtils.getMessage(Message.BUY_PRICE_TOO_LOW, new Replacement(Placeholder.MIN_PRICE, String.valueOf(amount * minPrice)))); @@ -316,16 +346,16 @@ class ShopCommandExecutor implements CommandExecutor { // Check if prices higher than maximum price for (String key :Config.maximumPrices) { - ItemStack itemStack = ItemUtils.getItemStack(key); + ItemStack is = ItemUtils.getItemStack(key); double maxPrice = plugin.getConfig().getDouble("maximum-prices." + key); - if (itemStack == null) { + if (is == null) { plugin.getLogger().warning("Invalid item found in maximum-prices: " + key); plugin.debug("Invalid item in maximum-prices: " + key); continue; } - if (itemStack.getType().equals(inHand.getType()) && itemStack.getDurability() == inHand.getDurability()) { + if (is.getType().equals(itemStack.getType()) && is.getDurability() == itemStack.getDurability()) { if (buyEnabled) { if ((buyPrice > amount * maxPrice) && (buyPrice > 0)) { p.sendMessage(LanguageUtils.getMessage(Message.BUY_PRICE_TOO_HIGH, new Replacement(Placeholder.MAX_PRICE, String.valueOf(amount * maxPrice)))); @@ -355,8 +385,8 @@ class ShopCommandExecutor implements CommandExecutor { } } - if (Enchantment.DURABILITY.canEnchantItem(inHand)) { - if (inHand.getDurability() > 0 && !Config.allowBrokenItems) { + if (Enchantment.DURABILITY.canEnchantItem(itemStack)) { + if (itemStack.getDurability() > 0 && !Config.allowBrokenItems) { p.sendMessage(LanguageUtils.getMessage(Message.CANNOT_SELL_BROKEN_ITEM)); plugin.debug(p.getName() + "'s item is broken"); return; @@ -372,12 +402,12 @@ class ShopCommandExecutor implements CommandExecutor { } } - ShopProduct product = new ShopProduct(inHand, amount); + ShopProduct product = new ShopProduct(itemStack, amount); ShopPreCreateEvent event = new ShopPreCreateEvent(p, new Shop(plugin, p, product, null, buyPrice, sellPrice, shopType)); Bukkit.getPluginManager().callEvent(event); if (!event.isCancelled()) { - ClickType.setPlayerClickType(p, new ClickType(ClickType.EnumClickType.CREATE, product, buyPrice, sellPrice, shopType)); + ClickType.setPlayerClickType(p, new CreateClickType(product, buyPrice, sellPrice, shopType)); plugin.debug(p.getName() + " can now click a chest"); p.sendMessage(LanguageUtils.getMessage(Message.CLICK_CHEST_CREATE)); } else { diff --git a/src/main/java/de/epiceric/shopchest/config/Config.java b/src/main/java/de/epiceric/shopchest/config/Config.java index 402a8e1..a9a3ff5 100644 --- a/src/main/java/de/epiceric/shopchest/config/Config.java +++ b/src/main/java/de/epiceric/shopchest/config/Config.java @@ -270,6 +270,11 @@ public class Config { **/ public static boolean autoCalculateItemAmount; + /** + * Whether players should be able to select an item from the creative inventory + */ + public static boolean creativeSelectItem; + /** *

Whether the mouse buttons are inverted

* Default:
@@ -486,6 +491,7 @@ public class Config { allowDecimalsInPrice = plugin.getConfig().getBoolean("allow-decimals-in-price"); allowBrokenItems = plugin.getConfig().getBoolean("allow-broken-items"); autoCalculateItemAmount = (allowDecimalsInPrice && plugin.getConfig().getBoolean("auto-calculate-item-amount")); + creativeSelectItem = plugin.getConfig().getBoolean("creative-select-item"); blacklist = (plugin.getConfig().getStringList("blacklist") == null) ? new ArrayList() : plugin.getConfig().getStringList("blacklist"); buyGreaterOrEqualSell = plugin.getConfig().getBoolean("buy-greater-or-equal-sell"); hopperProtection = plugin.getConfig().getBoolean("hopper-protection"); diff --git a/src/main/java/de/epiceric/shopchest/language/LanguageUtils.java b/src/main/java/de/epiceric/shopchest/language/LanguageUtils.java index 78047cc..3ae6552 100644 --- a/src/main/java/de/epiceric/shopchest/language/LanguageUtils.java +++ b/src/main/java/de/epiceric/shopchest/language/LanguageUtils.java @@ -2226,6 +2226,9 @@ public class LanguageUtils { messages.add(new LocalizedMessage(Message.OCCUPIED_SHOP_SLOTS, langConfig.getString("message.occupied-shop-slots", "&6You have &c%AMOUNT%/%LIMIT% &6shop slot/s occupied."))); messages.add(new LocalizedMessage(Message.CANNOT_SELL_ITEM, langConfig.getString("message.cannot-sell-item", "&cYou cannot create a shop with this item."))); messages.add(new LocalizedMessage(Message.USE_IN_CREATIVE, langConfig.getString("message.use-in-creative", "&cYou cannot use a shop in creative mode."))); + messages.add(new LocalizedMessage(Message.SELECT_ITEM, langConfig.getString("message.select-item", "&aOpen your inventory, and drop an item to select it."))); + messages.add(new LocalizedMessage(Message.ITEM_SELECTED, langConfig.getString("message.item-selected", "&aItem has been selected: &6%ITEMNAME%"))); + messages.add(new LocalizedMessage(Message.CREATION_CANCELLED, langConfig.getString("message.creation-cancelled", "&cShop creation has been cancelled."))); messages.add(new LocalizedMessage(Message.UPDATE_AVAILABLE, langConfig.getString("message.update.update-available", "&6&lVersion &c%VERSION% &6of &cShopChest &6is available &chere."))); messages.add(new LocalizedMessage(Message.UPDATE_CLICK_TO_DOWNLOAD, langConfig.getString("message.update.click-to-download", "Click to download"))); messages.add(new LocalizedMessage(Message.UPDATE_NO_UPDATE, langConfig.getString("message.update.no-update", "&6&lNo new update available."))); diff --git a/src/main/java/de/epiceric/shopchest/language/Message.java b/src/main/java/de/epiceric/shopchest/language/Message.java index df05442..9e8fc7d 100644 --- a/src/main/java/de/epiceric/shopchest/language/Message.java +++ b/src/main/java/de/epiceric/shopchest/language/Message.java @@ -56,6 +56,9 @@ public enum Message { OCCUPIED_SHOP_SLOTS, CANNOT_SELL_ITEM, USE_IN_CREATIVE, + SELECT_ITEM, + ITEM_SELECTED, + CREATION_CANCELLED, UPDATE_AVAILABLE, UPDATE_CLICK_TO_DOWNLOAD, UPDATE_NO_UPDATE, diff --git a/src/main/java/de/epiceric/shopchest/listeners/CreativeModeListener.java b/src/main/java/de/epiceric/shopchest/listeners/CreativeModeListener.java new file mode 100644 index 0000000..f2d4cff --- /dev/null +++ b/src/main/java/de/epiceric/shopchest/listeners/CreativeModeListener.java @@ -0,0 +1,185 @@ +package de.epiceric.shopchest.listeners; + +import org.bukkit.Material; +import org.bukkit.entity.Entity; +import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockMultiPlaceEvent; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.event.inventory.InventoryDragEvent; +import org.bukkit.event.inventory.InventoryMoveItemEvent; +import org.bukkit.event.player.PlayerInteractAtEntityEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.event.player.PlayerPickupItemEvent; +import org.bukkit.event.player.PlayerQuitEvent; + +import de.epiceric.shopchest.ShopChest; +import de.epiceric.shopchest.config.Placeholder; +import de.epiceric.shopchest.language.LanguageUtils; +import de.epiceric.shopchest.language.Message; +import de.epiceric.shopchest.language.Replacement; +import de.epiceric.shopchest.utils.ClickType; +import de.epiceric.shopchest.utils.ClickType.SelectClickType; + +public class CreativeModeListener implements Listener { + private ShopChest plugin; + + public CreativeModeListener(ShopChest plugin) { + this.plugin = plugin; + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onInventoryClick(InventoryClickEvent e) { + HumanEntity entity = e.getWhoClicked(); + if (!(entity instanceof Player)) + return; + + Player p = (Player) entity; + + ClickType clickType = ClickType.getPlayerClickType(p); + if (clickType instanceof SelectClickType) { + e.setCancelled(true); + + if (e.getCursor() == null || e.getCursor().getType() == Material.AIR) + return; + + ClickType.removePlayerClickType(p); + ((SelectClickType) clickType).setItem(e.getCursor()); + p.closeInventory(); + p.setGameMode(((SelectClickType) clickType).getGameMode()); + + p.sendMessage(LanguageUtils.getMessage(Message.ITEM_SELECTED, + new Replacement(Placeholder.ITEM_NAME, LanguageUtils.getItemName(e.getCursor())))); + plugin.getShopCommand().createShopAfterSelected(p, (SelectClickType) clickType); + } + } + + @EventHandler + public void onPlayerCloseInventory(InventoryCloseEvent e) { + HumanEntity entity = e.getPlayer(); + if (!(entity instanceof Player)) + return; + + Player p = (Player) entity; + + ClickType clickType = ClickType.getPlayerClickType(p); + if (!(clickType instanceof SelectClickType)) + return; + + ClickType.removePlayerClickType(p); + p.setGameMode(((SelectClickType) clickType).getGameMode()); + p.sendMessage(LanguageUtils.getMessage(Message.CREATION_CANCELLED)); + } + + @EventHandler + public void onPlayerQuit(PlayerQuitEvent e) { + // Reset game mode on quit if SelectClickType is set + Player p = e.getPlayer(); + ClickType ct = ClickType.getPlayerClickType(p); + if (ct instanceof SelectClickType) { + p.setGameMode(((SelectClickType) ct).getGameMode()); + } + ClickType.removePlayerClickType(p); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onInventoryDrag(InventoryDragEvent e) { + // Cancel any inventory drags if SelectClickType is set + HumanEntity entity = e.getWhoClicked(); + if (!(entity instanceof Player)) + return; + + ClickType clickType = ClickType.getPlayerClickType((Player) entity); + if (clickType instanceof SelectClickType) + e.setCancelled(true); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onInventoryMove(InventoryMoveItemEvent e) { + // Cancel any inventory movement if SelectClickType is set + if (e.getSource().getHolder() instanceof Player) { + Player p = (Player) e.getSource().getHolder(); + + ClickType clickType = ClickType.getPlayerClickType(p); + if (clickType instanceof SelectClickType) + e.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onPlayerPickup(PlayerPickupItemEvent e) { + // Cancel any item pickups if SelectClickType is set + ClickType clickType = ClickType.getPlayerClickType(e.getPlayer()); + if (clickType instanceof SelectClickType) + e.setCancelled(true); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onBlockBreak(BlockBreakEvent e) { + // Cancel any block breaks if SelectClickType is set + ClickType clickType = ClickType.getPlayerClickType(e.getPlayer()); + if (clickType instanceof SelectClickType) + e.setCancelled(true); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onBlockPlace(BlockPlaceEvent e) { + // Cancel any block places if SelectClickType is set + ClickType clickType = ClickType.getPlayerClickType(e.getPlayer()); + if (clickType instanceof SelectClickType) + e.setCancelled(true); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onBlockMultiPlace(BlockMultiPlaceEvent e) { + // Cancel any block places if SelectClickType is set + ClickType clickType = ClickType.getPlayerClickType(e.getPlayer()); + if (clickType instanceof SelectClickType) + e.setCancelled(true); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onPlayerInteract(PlayerInteractEvent e) { + // Cancel any interactions if SelectClickType is set + ClickType clickType = ClickType.getPlayerClickType(e.getPlayer()); + if (clickType instanceof SelectClickType) + e.setCancelled(true); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onPlayerInteractAtEntity(PlayerInteractAtEntityEvent e) { + // Cancel any entity interactions if SelectClickType is set + ClickType clickType = ClickType.getPlayerClickType(e.getPlayer()); + if (clickType instanceof SelectClickType) + e.setCancelled(true); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onPlayerDamageEntity(EntityDamageByEntityEvent e) { + // Cancel any entity damaging if SelectClickType is set + Entity entity = e.getDamager(); + if (!(entity instanceof Player)) + return; + + ClickType clickType = ClickType.getPlayerClickType((Player) entity); + if (clickType instanceof SelectClickType) + e.setCancelled(true); + } + + @EventHandler(priority = EventPriority.HIGHEST) + public void onPlayerMove(PlayerMoveEvent e) { + // Cancel any player movement if SelectClickType is set + ClickType clickType = ClickType.getPlayerClickType(e.getPlayer()); + if (clickType instanceof SelectClickType) + e.setCancelled(true); + } + +} \ No newline at end of file diff --git a/src/main/java/de/epiceric/shopchest/listeners/ShopInteractListener.java b/src/main/java/de/epiceric/shopchest/listeners/ShopInteractListener.java index 83d6a0f..6d636ea 100644 --- a/src/main/java/de/epiceric/shopchest/listeners/ShopInteractListener.java +++ b/src/main/java/de/epiceric/shopchest/listeners/ShopInteractListener.java @@ -26,6 +26,7 @@ import de.epiceric.shopchest.utils.ItemUtils; import de.epiceric.shopchest.utils.Permissions; import de.epiceric.shopchest.utils.ShopUtils; import de.epiceric.shopchest.utils.Utils; +import de.epiceric.shopchest.utils.ClickType.CreateClickType; import fr.xephi.authme.api.v3.AuthMeApi; import net.milkbowl.vault.economy.Economy; import net.milkbowl.vault.economy.EconomyResponse; @@ -68,7 +69,6 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; public class ShopInteractListener implements Listener { - private static final Pattern COLOR_CODE_PATTERN = Pattern.compile(".*([§]([a-fA-F0-9]))"); private static final Pattern FORMAT_CODE_PATTERN = Pattern.compile(".*([§]([l-oL-OkK]))"); @@ -120,7 +120,7 @@ public class ShopInteractListener implements Listener { if (e.getAction() != Action.RIGHT_CLICK_BLOCK) return; - if (ClickType.getPlayerClickType(p) == null) + if (!(ClickType.getPlayerClickType(p) instanceof CreateClickType)) return; if (b.getType() != Material.CHEST && b.getType() != Material.TRAPPED_CHEST) @@ -132,17 +132,17 @@ public class ShopInteractListener implements Listener { if (Config.enableAuthMeIntegration && plugin.hasAuthMe() && !AuthMeApi.getInstance().isAuthenticated(p)) return; - if (shopUtils.isShop(b.getLocation())) { - p.sendMessage(LanguageUtils.getMessage(Message.CHEST_ALREADY_SHOP)); - plugin.debug("Chest is already a shop"); - } else if (e.isCancelled() && !p.hasPermission(Permissions.CREATE_PROTECTED)) { + if (e.isCancelled() && !p.hasPermission(Permissions.CREATE_PROTECTED)) { p.sendMessage(LanguageUtils.getMessage(Message.NO_PERMISSION_CREATE_PROTECTED)); plugin.debug(p.getName() + " is not allowed to create a shop on the selected chest"); + } else if (shopUtils.isShop(b.getLocation())) { + p.sendMessage(LanguageUtils.getMessage(Message.CHEST_ALREADY_SHOP)); + plugin.debug("Chest is already a shop"); } else if (!ItemUtils.isAir(b.getRelative(BlockFace.UP).getType())) { p.sendMessage(LanguageUtils.getMessage(Message.CHEST_BLOCKED)); plugin.debug("Chest is blocked"); } else { - ClickType clickType = ClickType.getPlayerClickType(p); + CreateClickType clickType = (CreateClickType) ClickType.getPlayerClickType(p); ShopProduct product = clickType.getProduct(); double buyPrice = clickType.getBuyPrice(); double sellPrice = clickType.getSellPrice(); @@ -171,32 +171,35 @@ public class ShopInteractListener implements Listener { if (b.getType() != Material.CHEST && b.getType() != Material.TRAPPED_CHEST) return; - if (ClickType.getPlayerClickType(p) != null) { + ClickType clickType = ClickType.getPlayerClickType(p); + if (clickType != null) { if (e.getAction() != Action.RIGHT_CLICK_BLOCK) return; Shop shop = shopUtils.getShop(b.getLocation()); - if (shop == null && ClickType.getPlayerClickType(p).getClickType() != ClickType.EnumClickType.CREATE) { - p.sendMessage(LanguageUtils.getMessage(Message.CHEST_NO_SHOP)); - plugin.debug("Chest is not a shop"); - return; + switch (clickType.getClickType()) { + case CREATE: + case SELECT_ITEM: + break; + default: + if (shop == null) { + p.sendMessage(LanguageUtils.getMessage(Message.CHEST_NO_SHOP)); + plugin.debug("Chest is not a shop"); + return; + } } - switch (ClickType.getPlayerClickType(p).getClickType()) { - case CREATE: - return; - + switch (clickType.getClickType()) { case INFO: info(p, shop); break; - case REMOVE: remove(p, shop); break; - case OPEN: open(p, shop, true); break; + default: return; } e.setCancelled(true); diff --git a/src/main/java/de/epiceric/shopchest/utils/ClickType.java b/src/main/java/de/epiceric/shopchest/utils/ClickType.java index dbf7765..45f1c62 100644 --- a/src/main/java/de/epiceric/shopchest/utils/ClickType.java +++ b/src/main/java/de/epiceric/shopchest/utils/ClickType.java @@ -3,7 +3,12 @@ package de.epiceric.shopchest.utils; import de.epiceric.shopchest.ShopChest; import de.epiceric.shopchest.shop.ShopProduct; import de.epiceric.shopchest.shop.Shop.ShopType; + +import org.bukkit.Bukkit; +import org.bukkit.GameMode; import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.scheduler.BukkitTask; @@ -13,26 +18,27 @@ import java.util.Optional; import java.util.UUID; public class ClickType { - private static Map playerClickType = new HashMap<>(); private static Map playerTimers = new HashMap<>(); private EnumClickType enumClickType; - private ShopProduct product; - private double buyPrice; - private double sellPrice; - private ShopType shopType; public ClickType(EnumClickType enumClickType) { this.enumClickType = enumClickType; } - public ClickType(EnumClickType enumClickType, ShopProduct product, double buyPrice, double sellPrice, ShopType shopType) { - this.enumClickType = enumClickType; - this.product = product; - this.sellPrice = sellPrice; - this.buyPrice = buyPrice; - this.shopType = shopType; + /** + * Clear click types, cancel timers, and reset game modes + */ + public static void clear() { + playerClickType.forEach((uuid, ct) -> { + if (ct instanceof SelectClickType) { + Player p = Bukkit.getPlayer(uuid); + if (p != null) + p.setGameMode(((SelectClickType) ct).getGameMode()); + } + }); + playerTimers.forEach((uuid, timer) -> timer.cancel()); } /** @@ -47,12 +53,13 @@ public class ClickType { /** * Removes the click type from a player and cancels the 15 second timer + * * @param player Player to remove the click type from */ public static void removePlayerClickType(OfflinePlayer player) { UUID uuid = player.getUniqueId(); playerClickType.remove(uuid); - + // If a timer is still running, cancel it Optional.ofNullable(playerTimers.get(uuid)).ifPresent(task -> task.cancel()); playerTimers.remove(uuid); @@ -71,13 +78,15 @@ public class ClickType { // If a timer is already running, cancel it Optional.ofNullable(playerTimers.get(uuid)).ifPresent(task -> task.cancel()); - // Remove ClickType after 15 seconds if player has not clicked a chest - playerTimers.put(uuid, new BukkitRunnable() { - @Override - public void run() { - playerClickType.remove(uuid); - } - }.runTaskLater(ShopChest.getInstance(), 300)); + if (clickType.getClickType() != EnumClickType.SELECT_ITEM) { + // Remove ClickType after 15 seconds if player has not clicked a chest + playerTimers.put(uuid, new BukkitRunnable() { + @Override + public void run() { + playerClickType.remove(uuid); + } + }.runTaskLater(ShopChest.getInstance(), 300)); + } } /** @@ -87,36 +96,119 @@ public class ClickType { return enumClickType; } - /** - * @return If {@link #getClickType()} returns {@link EnumClickType#CREATE}, this returns the item, the player has hold in his hands, else null. - */ - public ShopProduct getProduct() { - return product; - } - - /** - * @return If {@link #getClickType()} returns {@link EnumClickType#CREATE}, this returns the buy price, the player has entered, else null. - */ - public double getBuyPrice() { - return buyPrice; - } - - /** - * @return If {@link #getClickType()} returns {@link EnumClickType#CREATE}, this returns the sell price, the player has entered, else null. - */ - public double getSellPrice() { - return sellPrice; - } - - /** - * @return If {@link #getClickType()} returns {@link EnumClickType#CREATE}, this returns the shop type, the player has entered, else null. - */ - public ShopType getShopType() { - return shopType; - } - public enum EnumClickType { - CREATE, REMOVE, INFO, OPEN + CREATE, REMOVE, INFO, OPEN, SELECT_ITEM + } + + public static class CreateClickType extends ClickType { + private ShopProduct product; + private double buyPrice; + private double sellPrice; + private ShopType shopType; + + public CreateClickType(ShopProduct product, double buyPrice, double sellPrice, ShopType shopType) { + super(EnumClickType.CREATE); + this.product = product; + this.sellPrice = sellPrice; + this.buyPrice = buyPrice; + this.shopType = shopType; + } + + /** + * Returns the item, the player has hold in his hands + */ + public ShopProduct getProduct() { + return product; + } + + /** + * Returns the buy price, the player has entered + */ + public double getBuyPrice() { + return buyPrice; + } + + /** + * Returns the sell price, the player has entered + */ + public double getSellPrice() { + return sellPrice; + } + + /** + * Returns the shop type, the player has entered + */ + public ShopType getShopType() { + return shopType; + } + } + + public static class SelectClickType extends ClickType { + private ItemStack itemStack; + private GameMode gameMode; + private int amount; + private double buyPrice; + private double sellPrice; + private ShopType shopType; + + public SelectClickType(GameMode gameMode, int amount, double buyPrice, double sellPrice, ShopType shopType) { + super(EnumClickType.SELECT_ITEM); + this.gameMode = gameMode; + this.amount = amount; + this.sellPrice = sellPrice; + this.buyPrice = buyPrice; + this.shopType = shopType; + } + + /** + * Returns the selected item (or {@code null} if no item has been selected) + */ + public ItemStack getItem() { + return itemStack; + } + + /** + * Sets the selected item + * @param itemStack The item to set as selected + */ + public void setItem(ItemStack itemStack) { + this.itemStack = itemStack; + } + + /** + * Returns the gamemode, the player was in before entering creative mode + */ + public GameMode getGameMode() { + return gameMode; + } + + /** + * Returns the amount, the player has entered + */ + public int getAmount() { + return amount; + } + + /** + * Returns the buy price, the player has entered + */ + public double getBuyPrice() { + return buyPrice; + } + + /** + * Returns the sell price, the player has entered + */ + public double getSellPrice() { + return sellPrice; + } + + /** + * Returns the shop type, the player has entered + */ + public ShopType getShopType() { + return shopType; + } } } diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index db60dd6..f920edb 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -24,6 +24,10 @@ shop-info-item: "STICK" # in order to prevent accidental purchases or sells. confirm-shopping: false +# Set whether players should be able to select the shop item from the +# creative inventory if they don't hold an item in their hand. +creative-select-item: true + # Set whether the shop creation price should be refunded # when the shop is removed. # This only applies when the creator of the shop removes it himself diff --git a/src/main/resources/lang/de_DE-legacy.lang b/src/main/resources/lang/de_DE-legacy.lang index 9f8b0f4..e996a3f 100644 --- a/src/main/resources/lang/de_DE-legacy.lang +++ b/src/main/resources/lang/de_DE-legacy.lang @@ -53,6 +53,9 @@ message.shop-limit-reached=&cDu hast dein Limit von &6%LIMIT% &cShop/s erreicht. message.occupied-shop-slots=&6Du hast &c%AMOUNT%/%LIMIT% &6Shop Slot/s benutzt. message.cannot-sell-item=&cDu kannst für diesen Artikel keinen Shop erstellen. message.use-in-creative=&cDu kannst im Kreativ-Modus keine Shops benutzen. +message.select-item=&aÖffne dein Inventar und lass ein Item fallen, um es auszuwählen. +message.item-selected=&aItem wurde ausgewählt: &6%ITEMNAME% +message.creation-cancelled=&cShoperstellung wurde abgebrochen. message.update.update-available=&6&lVersion &c&l%VERSION% &6&lvon &c&lShopChest &6&list verfügbar. message.update.click-to-download=Klicke hier zum Herunterladen message.update.no-update=&6&lKeine neue Aktualisierung verfügbar. diff --git a/src/main/resources/lang/de_DE.lang b/src/main/resources/lang/de_DE.lang index 0e5c484..3b584be 100644 --- a/src/main/resources/lang/de_DE.lang +++ b/src/main/resources/lang/de_DE.lang @@ -53,6 +53,9 @@ message.shop-limit-reached=&cDu hast dein Limit von &6%LIMIT% &cShop/s erreicht. message.occupied-shop-slots=&6Du hast &c%AMOUNT%/%LIMIT% &6Shop Slot/s benutzt. message.cannot-sell-item=&cDu kannst für diesen Artikel keinen Shop erstellen. message.use-in-creative=&cDu kannst im Kreativ-Modus keine Shops benutzen. +message.select-item=&aÖffne dein Inventar und lass ein Item fallen, um es auszuwählen. +message.item-selected=&aItem wurde ausgewählt: &6%ITEMNAME% +message.creation-cancelled=&cShoperstellung wurde abgebrochen. message.update.update-available=&6&lVersion &c&l%VERSION% &6&lvon &c&lShopChest &6&list verfügbar. message.update.click-to-download=Klicke hier zum Herunterladen message.update.no-update=&6&lKeine neue Aktualisierung verfügbar. diff --git a/src/main/resources/lang/en_US-legacy.lang b/src/main/resources/lang/en_US-legacy.lang index 239230a..808af58 100644 --- a/src/main/resources/lang/en_US-legacy.lang +++ b/src/main/resources/lang/en_US-legacy.lang @@ -198,6 +198,16 @@ message.cannot-sell-item=&cYou cannot create a shop with this item. # Set the message when the player tries to use a shop in creative mode. message.use-in-creative=&cYou cannot use a shop in creative mode. +# Set the message when the player has to select an item from the creative menu. +message.select-item=&aOpen your inventory, and drop an item to select it. + +# Set the message when the player has selected an item from the creative menu. +# Usable Placeholders: %ITEMNAME% +message.item-selected=&aItem has been selected: &6%ITEMNAME% + +# Set the message when the player closes the creative menu to select an item. +message.creation-cancelled=&cShop creation has been cancelled. + # Set the message when an update is available. # Usable Placeholders: %VERSION% message.update.update-available=&6&lVersion &c&l%VERSION% &6&lof &c&lShopChest &6&lis available &c&lhere. diff --git a/src/main/resources/lang/en_US.lang b/src/main/resources/lang/en_US.lang index 49a46aa..94af73a 100644 --- a/src/main/resources/lang/en_US.lang +++ b/src/main/resources/lang/en_US.lang @@ -191,6 +191,16 @@ message.cannot-sell-item=&cYou cannot create a shop with this item. # Set the message when the player tries to use a shop in creative mode. message.use-in-creative=&cYou cannot use a shop in creative mode. +# Set the message when the player has to select an item from the creative menu. +message.select-item=&aOpen your inventory, and drop an item to select it. + +# Set the message when the player has selected an item from the creative menu. +# Usable Placeholders: %ITEMNAME% +message.item-selected=&aItem has been selected: &6%ITEMNAME% + +# Set the message when the player closes the creative menu to select an item. +message.creation-cancelled=&cShop creation has been cancelled. + # Set the message when an update is available. # Usable Placeholders: %VERSION% message.update.update-available=&6&lVersion &c&l%VERSION% &6&lof &c&lShopChest &6&lis available &c&lhere.