diff --git a/src/main/java/de/epiceric/shopchest/config/Config.java b/src/main/java/de/epiceric/shopchest/config/Config.java index 7b170d4..b6c6b6e 100644 --- a/src/main/java/de/epiceric/shopchest/config/Config.java +++ b/src/main/java/de/epiceric/shopchest/config/Config.java @@ -104,6 +104,9 @@ public class Config { /** Whether shops should be protected by explosions **/ public boolean explosion_protection; + /** Whether buys and sells must be confirmed **/ + public boolean confirm_shopping; + /** Whether quality mode should be enabled **/ public boolean enable_quality_mode; @@ -365,6 +368,8 @@ public class Config { blacklist = (plugin.getConfig().getStringList("blacklist") == null) ? new ArrayList() : plugin.getConfig().getStringList("blacklist"); buy_greater_or_equal_sell = plugin.getConfig().getBoolean("buy-greater-or-equal-sell"); hopper_protection = plugin.getConfig().getBoolean("hopper-protection"); + explosion_protection = plugin.getConfig().getBoolean("explosion-protection"); + confirm_shopping = plugin.getConfig().getBoolean("confirm-shopping"); enable_quality_mode = plugin.getConfig().getBoolean("enable-quality-mode"); enable_hologram_interaction = plugin.getConfig().getBoolean("enable-hologram-interaction"); enable_debug_log = plugin.getConfig().getBoolean("enable-debug-log"); @@ -379,7 +384,6 @@ public class Config { enable_griefprevention_integration = plugin.getConfig().getBoolean("enable-griefprevention-integration"); enable_areashop_integration = plugin.getConfig().getBoolean("enable-areashop-integration"); enable_vendor_messages = plugin.getConfig().getBoolean("enable-vendor-messages"); - explosion_protection = plugin.getConfig().getBoolean("explosion-protection"); only_show_shops_in_sight = plugin.getConfig().getBoolean("only-show-shops-in-sight"); only_show_first_shop_in_sight = plugin.getConfig().getBoolean("only-show-first-shop-in-sight"); exclude_admin_shops = plugin.getConfig().getBoolean("shop-limits.exclude-admin-shops"); diff --git a/src/main/java/de/epiceric/shopchest/language/LanguageUtils.java b/src/main/java/de/epiceric/shopchest/language/LanguageUtils.java index eda0bef..7269fa2 100644 --- a/src/main/java/de/epiceric/shopchest/language/LanguageUtils.java +++ b/src/main/java/de/epiceric/shopchest/language/LanguageUtils.java @@ -1043,6 +1043,7 @@ public class LanguageUtils { messages.add(new LocalizedMessage(LocalizedMessage.Message.CLICK_CHEST_REMOVE, langConfig.getString("message.click-chest-to-remove-shop", "&aClick a shop within 15 seconds to remove it."))); messages.add(new LocalizedMessage(LocalizedMessage.Message.CLICK_CHEST_INFO, langConfig.getString("message.click-chest-for-info", "&aClick a shop within 15 seconds to retrieve information."))); messages.add(new LocalizedMessage(LocalizedMessage.Message.CLICK_CHEST_OPEN, langConfig.getString("message.click-chest-to-open-shop", "&aClick a shop within 15 seconds to open it."))); + messages.add(new LocalizedMessage(LocalizedMessage.Message.CLICK_TO_CONFIRM, langConfig.getString("message.click-to-confirm", "&aClick again to confirm."))); messages.add(new LocalizedMessage(LocalizedMessage.Message.OPENED_SHOP, langConfig.getString("message.opened-shop", "&aYou opened %VENDOR%'s shop."))); messages.add(new LocalizedMessage(LocalizedMessage.Message.CANNOT_BREAK_SHOP, langConfig.getString("message.cannot-break-shop", "&cYou can't break a shop."))); messages.add(new LocalizedMessage(LocalizedMessage.Message.CANNOT_SELL_BROKEN_ITEM, langConfig.getString("message.cannot-sell-broken-item", "&cYou can't sell a broken item."))); diff --git a/src/main/java/de/epiceric/shopchest/language/LocalizedMessage.java b/src/main/java/de/epiceric/shopchest/language/LocalizedMessage.java index 074d9bc..519b549 100644 --- a/src/main/java/de/epiceric/shopchest/language/LocalizedMessage.java +++ b/src/main/java/de/epiceric/shopchest/language/LocalizedMessage.java @@ -74,6 +74,7 @@ public class LocalizedMessage { CLICK_CHEST_REMOVE, CLICK_CHEST_INFO, CLICK_CHEST_OPEN, + CLICK_TO_CONFIRM, OPENED_SHOP, CANNOT_BREAK_SHOP, CANNOT_SELL_BROKEN_ITEM, diff --git a/src/main/java/de/epiceric/shopchest/listeners/ShopInteractListener.java b/src/main/java/de/epiceric/shopchest/listeners/ShopInteractListener.java index 446dd67..abaf351 100644 --- a/src/main/java/de/epiceric/shopchest/listeners/ShopInteractListener.java +++ b/src/main/java/de/epiceric/shopchest/listeners/ShopInteractListener.java @@ -70,7 +70,10 @@ import pl.islandworld.api.IslandWorldApi; import us.talabrek.ultimateskyblock.api.IslandInfo; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; +import java.util.UUID; public class ShopInteractListener implements Listener { @@ -326,6 +329,8 @@ public class ShopInteractListener implements Listener { } } + private Map> needsConfirmation = new HashMap<>(); + private void handleInteractEvent(PlayerInteractEvent e) { Block b = e.getClickedBlock(); Player p = e.getPlayer(); @@ -339,27 +344,19 @@ public class ShopInteractListener implements Listener { if (b.getType().equals(Material.CHEST) || b.getType().equals(Material.TRAPPED_CHEST)) { if (ClickType.getPlayerClickType(p) != null) { if (e.getAction() == Action.RIGHT_CLICK_BLOCK) { + Shop shop = shopUtils.getShop(b.getLocation()); + if (shop != null || ClickType.getPlayerClickType(p).getClickType() == ClickType.EnumClickType.CREATE) { + switch (ClickType.getPlayerClickType(p).getClickType()) { + case INFO: + e.setCancelled(true); - switch (ClickType.getPlayerClickType(p).getClickType()) { - case INFO: - e.setCancelled(true); - - if (shopUtils.isShop(b.getLocation())) { - Shop shop = shopUtils.getShop(b.getLocation()); info(p, shop); - } else { - p.sendMessage(LanguageUtils.getMessage(LocalizedMessage.Message.CHEST_NO_SHOP)); - plugin.debug("Chest is not a shop"); - } - ClickType.removePlayerClickType(p); - break; + ClickType.removePlayerClickType(p); + break; - case REMOVE: - e.setCancelled(true); - - if (shopUtils.isShop(b.getLocation())) { - Shop shop = shopUtils.getShop(b.getLocation()); + case REMOVE: + e.setCancelled(true); if (shop.getShopType() == ShopType.ADMIN) { if (p.hasPermission(Permissions.REMOVE_ADMIN)) { @@ -376,46 +373,46 @@ public class ShopInteractListener implements Listener { plugin.debug(p.getName() + " is not permitted to remove another player's shop"); } } - } else { - p.sendMessage(LanguageUtils.getMessage(LocalizedMessage.Message.CHEST_NO_SHOP)); - plugin.debug("Chest is not a shop"); - } - ClickType.removePlayerClickType(p); - break; + ClickType.removePlayerClickType(p); + break; - case OPEN: - e.setCancelled(true); + case OPEN: + e.setCancelled(true); - if (shopUtils.isShop(b.getLocation())) { - Shop shop = shopUtils.getShop(b.getLocation()); if (p.getUniqueId().equals(shop.getVendor().getUniqueId()) || p.hasPermission(Permissions.OPEN_OTHER)) { open(p, shop, true); } else { p.sendMessage(LanguageUtils.getMessage(LocalizedMessage.Message.NO_PERMISSION_OPEN_OTHERS)); plugin.debug(p.getName() + " is not permitted to open another player's shop"); } - } else { - p.sendMessage(LanguageUtils.getMessage(LocalizedMessage.Message.CHEST_NO_SHOP)); - plugin.debug("Chest is not a shop"); - } - ClickType.removePlayerClickType(p); - break; + ClickType.removePlayerClickType(p); + break; + } + } else { + p.sendMessage(LanguageUtils.getMessage(LocalizedMessage.Message.CHEST_NO_SHOP)); + plugin.debug("Chest is not a shop"); } } } else { Shop shop = shopUtils.getShop(b.getLocation()); + + boolean confirmed = needsConfirmation.containsKey(p.getUniqueId()) && needsConfirmation.get(p.getUniqueId()).contains(shop.getID()); + if (shop != null) { if (e.getAction() == Action.LEFT_CLICK_BLOCK && p.isSneaking() && Utils.hasAxeInHand(p)) { return; } + ItemStack infoItem = config.shop_info_item; if (infoItem != null) { if (e.getAction() == Action.RIGHT_CLICK_BLOCK || e.getAction() == Action.LEFT_CLICK_BLOCK) { ItemStack item = Utils.getItemInMainHand(p); + if (item == null || !(infoItem.getType() == item.getType() && infoItem.getDurability() == item.getDurability())) { item = Utils.getItemInOffHand(p); + if (item != null && infoItem.getType() == item.getType() && infoItem.getDurability() == item.getDurability()) { e.setCancelled(true); info(p, shop); @@ -468,7 +465,21 @@ public class ShopInteractListener implements Listener { if (shop.getShopType() == ShopType.ADMIN) { if (externalPluginsAllowed || p.hasPermission(Permissions.BYPASS_EXTERNAL_PLUGIN)) { - buy(p, shop, p.isSneaking()); + if (confirmed || !config.confirm_shopping) { + buy(p, shop, p.isSneaking()); + if (config.confirm_shopping) { + Set ids = needsConfirmation.containsKey(p.getUniqueId()) ? needsConfirmation.get(p.getUniqueId()) : new HashSet(); + ids.remove(shop.getID()); + if (ids.isEmpty()) needsConfirmation.remove(p.getUniqueId()); + else needsConfirmation.put(p.getUniqueId(), ids); + } + } else { + plugin.debug("Needs confirmation"); + p.sendMessage(LanguageUtils.getMessage(LocalizedMessage.Message.CLICK_TO_CONFIRM)); + Set ids = needsConfirmation.containsKey(p.getUniqueId()) ? needsConfirmation.get(p.getUniqueId()) : new HashSet(); + ids.add(shop.getID()); + needsConfirmation.put(p.getUniqueId(), ids); + } } else { plugin.debug(p.getName() + " doesn't have external plugin's permission"); p.sendMessage(LanguageUtils.getMessage(LocalizedMessage.Message.NO_PERMISSION_BUY_HERE)); @@ -479,10 +490,38 @@ public class ShopInteractListener implements Listener { int amount = (p.isSneaking() ? shop.getProduct().getMaxStackSize() : shop.getProduct().getAmount()); if (Utils.getAmount(c.getInventory(), shop.getProduct()) >= amount) { - buy(p, shop, p.isSneaking()); + if (confirmed || !config.confirm_shopping) { + buy(p, shop, p.isSneaking()); + if (config.confirm_shopping) { + Set ids = needsConfirmation.containsKey(p.getUniqueId()) ? needsConfirmation.get(p.getUniqueId()) : new HashSet(); + ids.remove(shop.getID()); + if (ids.isEmpty()) needsConfirmation.remove(p.getUniqueId()); + else needsConfirmation.put(p.getUniqueId(), ids); + } + } else { + plugin.debug("Needs confirmation"); + p.sendMessage(LanguageUtils.getMessage(LocalizedMessage.Message.CLICK_TO_CONFIRM)); + Set ids = needsConfirmation.containsKey(p.getUniqueId()) ? needsConfirmation.get(p.getUniqueId()) : new HashSet(); + ids.add(shop.getID()); + needsConfirmation.put(p.getUniqueId(), ids); + } } else { if (config.auto_calculate_item_amount && Utils.getAmount(c.getInventory(), shop.getProduct()) > 0) { - buy(p, shop, p.isSneaking()); + if (confirmed || !config.confirm_shopping) { + buy(p, shop, p.isSneaking()); + if (config.confirm_shopping) { + Set ids = needsConfirmation.containsKey(p.getUniqueId()) ? needsConfirmation.get(p.getUniqueId()) : new HashSet(); + ids.remove(shop.getID()); + if (ids.isEmpty()) needsConfirmation.remove(p.getUniqueId()); + else needsConfirmation.put(p.getUniqueId(), ids); + } + } else { + plugin.debug("Needs confirmation"); + p.sendMessage(LanguageUtils.getMessage(LocalizedMessage.Message.CLICK_TO_CONFIRM)); + Set ids = needsConfirmation.containsKey(p.getUniqueId()) ? needsConfirmation.get(p.getUniqueId()) : new HashSet(); + ids.add(shop.getID()); + needsConfirmation.put(p.getUniqueId(), ids); + } } else { p.sendMessage(LanguageUtils.getMessage(LocalizedMessage.Message.OUT_OF_STOCK)); if (shop.getVendor().isOnline() && config.enable_vendor_messages) { @@ -541,10 +580,38 @@ public class ShopInteractListener implements Listener { int amount = stack ? shop.getProduct().getMaxStackSize() : shop.getProduct().getAmount(); if (Utils.getAmount(p.getInventory(), shop.getProduct()) >= amount) { - sell(p, shop, stack); + if (confirmed || !config.confirm_shopping) { + sell(p, shop, stack); + if (config.confirm_shopping) { + Set ids = needsConfirmation.containsKey(p.getUniqueId()) ? needsConfirmation.get(p.getUniqueId()) : new HashSet(); + ids.remove(shop.getID()); + if (ids.isEmpty()) needsConfirmation.remove(p.getUniqueId()); + else needsConfirmation.put(p.getUniqueId(), ids); + } + } else { + plugin.debug("Needs confirmation"); + p.sendMessage(LanguageUtils.getMessage(LocalizedMessage.Message.CLICK_TO_CONFIRM)); + Set ids = needsConfirmation.containsKey(p.getUniqueId()) ? needsConfirmation.get(p.getUniqueId()) : new HashSet(); + ids.add(shop.getID()); + needsConfirmation.put(p.getUniqueId(), ids); + } } else { if (config.auto_calculate_item_amount && Utils.getAmount(p.getInventory(), shop.getProduct()) > 0) { - sell(p, shop, stack); + if (confirmed || !config.confirm_shopping) { + sell(p, shop, stack); + if (config.confirm_shopping) { + Set ids = needsConfirmation.containsKey(p.getUniqueId()) ? needsConfirmation.get(p.getUniqueId()) : new HashSet(); + ids.remove(shop.getID()); + if (ids.isEmpty()) needsConfirmation.remove(p.getUniqueId()); + else needsConfirmation.put(p.getUniqueId(), ids); + } + } else { + plugin.debug("Needs confirmation"); + p.sendMessage(LanguageUtils.getMessage(LocalizedMessage.Message.CLICK_TO_CONFIRM)); + Set ids = needsConfirmation.containsKey(p.getUniqueId()) ? needsConfirmation.get(p.getUniqueId()) : new HashSet(); + ids.add(shop.getID()); + needsConfirmation.put(p.getUniqueId(), ids); + } } else { p.sendMessage(LanguageUtils.getMessage(LocalizedMessage.Message.NOT_ENOUGH_ITEMS)); plugin.debug(p.getName() + " doesn't have enough items"); diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index bb6011a..862ced7 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -27,6 +27,10 @@ update-quality: NORMAL # You can set this to an empty string to disable this feature. shop-info-item: "STICK" +# Set whether buys or sells need to be confirmed by the player +# in order to prevent accidental purchases or sells. +confirm-shopping: false + # Set whether interaction with the hologram should be enabled. # If set to true, a player can do the exact same thing with the # hologram, as with the chest. You can even open the chest if you diff --git a/src/main/resources/lang/de_DE.lang b/src/main/resources/lang/de_DE.lang index b754918..c55e612 100644 --- a/src/main/resources/lang/de_DE.lang +++ b/src/main/resources/lang/de_DE.lang @@ -44,6 +44,7 @@ message.click-chest-to-create-shop=&aKlicke innerhalb von 15 Sekunden auf eine T message.click-chest-to-remove-shop=&aKlicke innerhalb von 15 Sekunden auf einen Shop, um ihn zu entfernen. message.click-chest-for-info=&aKlicke innerhalb von 15 Sekunden auf einen Shop, um Informationen über ihn zu bekommen. message.click-chest-to-open-shop=&Klicke innerhalb von 15 Sekunden auf einen Shop, um ihn zu öffnen. +message.click-to-confirm=&aKlicke noch einmal zum Bestätigen. message.opened-shop=&aDu hast &6%VENDOR%&as Shop geöffnet. message.cannot-break-shop=&cDu kannst einen Shop nicht zerstören. message.cannot-sell-broken-item=&cDu kannst kein kaputtes Artikel verkaufen. diff --git a/src/main/resources/lang/en_US.lang b/src/main/resources/lang/en_US.lang index db1f652..d3e5413 100644 --- a/src/main/resources/lang/en_US.lang +++ b/src/main/resources/lang/en_US.lang @@ -161,6 +161,9 @@ message.click-chest-for-info=&aClick a shop within 15 seconds to retrieve inform # Set the message when the player must click a shop to open it. message.click-chest-to-open-shop=&aClick a shop within 15 seconds to open it. +# Set the message when the player must click the shop again to confirm the buy/sell. +message.click-to-confirm=&aClick again to confirm. + # Set the message when the player opened a shop. # Usable Placeholders: %VENDOR% message.opened-shop=&aYou opened %VENDOR%'s shop.