From bd3c245dfc59f4d11abcad5c75a61af99faafd93 Mon Sep 17 00:00:00 2001 From: KillerOfPie Date: Tue, 29 Mar 2022 21:55:14 -0700 Subject: [PATCH] Messaging and Text Display Updates + Bug Fixes - Added ITRADE_NO_COST_AMOUNT setting and FAILED_TRADE message - Changed INSUFFICIENT_ITEMS, SHOP_INSUFFICIENT_ITEMS, and ON_TRADE messages to show full trade details - Message file incremented to v1.3 - Added ItemMultiLin Message method - Changed ObjectHolder#toString to just pass Object#toString to reduce output size and readability - DRY'd Shop#areCostsValid and Shop#areProducts Valid - Fixed bug caused by Shop#hasProductStock and Shop#hasCostStock pulling directly from product/cost objects instead of Shop#getCost/Shop#getProduct(was causing inaccurate output from getItems the wasn't apparent until other changes were made) - Changed Utils#getItems to take Inventory#getStorageContents rather than Inventory - Added Static implementation of ShopItemStack#getCleanItemName to be used without creating new objects when not needed and redirected non-static method to use new static method(DRY) - Added support to ShopItemStack#getCleanItemName for book Titles and Proper formatting of ItemType returns - Added MISSING_ITEMS, RECEIVED_LINES, and GIVEN_ITEMS to Variables Enum - Added Utils#createBadList to clean up creation of `null as first index lists - Cleaned up some javaDocs and missed significantly more - Changed return of Utils#canExchangeAll to Tuple> to return bad lists in cases where ExchangeStatus indicates failure do to missing items. returns badList in all other cases - Cleaned up message calls in ShopTradeListener using new detailed messages. - Changed ShopTradeListener#tradeAllItems to return Tuple, List> as Good/Bad CostList, ProductList for detailed messages - Changed output for cost-free iTrade shops - Other Cleanup and Optimizations --- .../shanerx/tradeshop/enumys/DebugLevels.java | 4 +- .../listeners/ShopTradeListener.java | 151 ++++++++---------- .../org/shanerx/tradeshop/objects/Shop.java | 28 ++-- .../shanerx/tradeshop/objects/ShopChest.java | 2 +- .../tradeshop/objects/ShopItemStack.java | 14 +- .../shanerx/tradeshop/utils/ObjectHolder.java | 4 +- .../org/shanerx/tradeshop/utils/Utils.java | 128 ++++++++------- .../tradeshop/utils/config/Message.java | 80 +++++++++- .../tradeshop/utils/config/Setting.java | 1 + .../tradeshop/utils/config/Variable.java | 5 +- src/main/resources/Lang/en-us.yml | 11 +- 11 files changed, 255 insertions(+), 173 deletions(-) diff --git a/src/main/java/org/shanerx/tradeshop/enumys/DebugLevels.java b/src/main/java/org/shanerx/tradeshop/enumys/DebugLevels.java index 200aaf48..65167eb3 100644 --- a/src/main/java/org/shanerx/tradeshop/enumys/DebugLevels.java +++ b/src/main/java/org/shanerx/tradeshop/enumys/DebugLevels.java @@ -40,8 +40,8 @@ public enum DebugLevels { ITEM_COMPARE(6, Level.WARNING), // 32 NAME_COMPARE(7, Level.WARNING), // 64 SHULKERS_SUCK(8, Level.WARNING), // 128 - ENCHANT_CHECKS(9, Level.WARNING) // 256 - + ENCHANT_CHECKS(9, Level.WARNING), // 256 + OUTPUT(10, Level.WARNING) //512 ; //position is what value to check for this level in the binary string -1. diff --git a/src/main/java/org/shanerx/tradeshop/listeners/ShopTradeListener.java b/src/main/java/org/shanerx/tradeshop/listeners/ShopTradeListener.java index 997fa873..df54afef 100644 --- a/src/main/java/org/shanerx/tradeshop/listeners/ShopTradeListener.java +++ b/src/main/java/org/shanerx/tradeshop/listeners/ShopTradeListener.java @@ -26,6 +26,7 @@ package org.shanerx.tradeshop.listeners; import org.bukkit.Bukkit; +import org.bukkit.Material; import org.bukkit.block.BlockState; import org.bukkit.block.Sign; import org.bukkit.entity.Player; @@ -37,13 +38,14 @@ import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.shanerx.tradeshop.enumys.ExchangeStatus; import org.shanerx.tradeshop.enumys.Permissions; import org.shanerx.tradeshop.enumys.ShopType; import org.shanerx.tradeshop.framework.events.PlayerPreTradeEvent; import org.shanerx.tradeshop.framework.events.PlayerPrepareTradeEvent; import org.shanerx.tradeshop.framework.events.PlayerSuccessfulTradeEvent; import org.shanerx.tradeshop.objects.Shop; -import org.shanerx.tradeshop.objects.ShopItemStack; import org.shanerx.tradeshop.objects.ShopLocation; import org.shanerx.tradeshop.utils.Tuple; import org.shanerx.tradeshop.utils.Utils; @@ -51,8 +53,10 @@ import org.shanerx.tradeshop.utils.config.Setting; import org.shanerx.tradeshop.utils.config.Variable; -import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; public class ShopTradeListener extends Utils implements Listener { @@ -103,6 +107,8 @@ public void onBlockInteract(PlayerInteractEvent e) { Bukkit.getPluginManager().callEvent(preEvent); if (preEvent.isCancelled()) return; + boolean doBiTradeAlternate = shop.getShopType() == ShopType.BITRADE && e.getAction() == Action.LEFT_CLICK_BLOCK; + chestState = shop.getStorage(); if (!shop.getShopType().equals(ShopType.ITRADE) && chestState == null) { Message.MISSING_CHEST.sendMessage(buyer); @@ -115,20 +121,7 @@ public void onBlockInteract(PlayerInteractEvent e) { return; } - String productName = "", costName = ""; - int amountCost = 0, amountProduct = 0, multiplier = 1; - - for (ShopItemStack item : shop.getCost()) { //Shop cost list - //If item has custom name set to tempName, else set material name - costName = costName.isEmpty() ? item.getCleanItemName() : Message.VARIOUS_ITEM_TYPE.toString(); - amountCost += item.getItemStack().getAmount(); - } - - for (ShopItemStack item : shop.getProduct()) { //Shop product list - //If item has custom name set to tempName, else set material name - productName = productName.isEmpty() ? item.getCleanItemName() : Message.VARIOUS_ITEM_TYPE.toString(); - amountProduct += item.getItemStack().getAmount(); - } + int multiplier = 1; if (buyer.isSneaking() && Setting.ALLOW_MULTI_TRADE.getBoolean()) { multiplier = PLUGIN.getDataStorage().loadPlayer(buyer.getUniqueId()).getMulti(); @@ -141,11 +134,12 @@ public void onBlockInteract(PlayerInteractEvent e) { case INCOMPLETE: Message.SHOP_EMPTY.sendMessage(buyer); case OUT_OF_STOCK: - if (shop.getShopType() == ShopType.BITRADE && e.getAction() == Action.LEFT_CLICK_BLOCK) { - Message.SHOP_INSUFFICIENT_ITEMS.sendMessage(buyer, new Tuple<>(Variable.ITEM.toString(), costName), new Tuple<>(Variable.AMOUNT.toString(), String.valueOf(amountCost))); - } else { - Message.SHOP_INSUFFICIENT_ITEMS.sendMessage(buyer, new Tuple<>(Variable.ITEM.toString(), productName), new Tuple<>(Variable.AMOUNT.toString(), String.valueOf(amountProduct))); + if (shop.getShopType() == ShopType.ITRADE) { + break; } + + List searchResult = getItems(shop.getChestAsSC().getInventory().getStorageContents(), doBiTradeAlternate ? shop.getCost() : shop.getProduct(), multiplier); + Message.SHOP_INSUFFICIENT_ITEMS.sendItemMultiLineMessage(buyer, Collections.singletonMap(Variable.MISSING_ITEMS, searchResult)); return; case OPEN: break; @@ -158,34 +152,20 @@ public void onBlockInteract(PlayerInteractEvent e) { e.setCancelled(true); shop = PLUGIN.getDataStorage().loadShopFromSign(new ShopLocation(s.getLocation())); - switch (canExchangeAll(shop, buyer.getInventory(), multiplier, e.getAction())) { + Tuple> canExchangeResult = canExchangeAll(shop, buyer.getInventory(), multiplier, e.getAction()); + + switch (canExchangeResult.getLeft()) { case SHOP_NO_PRODUCT: - if (shop.getShopType() == ShopType.BITRADE && e.getAction() == Action.LEFT_CLICK_BLOCK) { - Message.SHOP_INSUFFICIENT_ITEMS.sendMessage(buyer, new Tuple<>(Variable.ITEM.toString(), costName), new Tuple<>(Variable.AMOUNT.toString(), String.valueOf(amountCost))); - } else { - Message.SHOP_INSUFFICIENT_ITEMS.sendMessage(buyer, new Tuple<>(Variable.ITEM.toString(), productName), new Tuple<>(Variable.AMOUNT.toString(), String.valueOf(amountProduct))); - } + Message.SHOP_INSUFFICIENT_ITEMS.sendItemMultiLineMessage(buyer, Collections.singletonMap(Variable.MISSING_ITEMS, canExchangeResult.getRight())); return; case PLAYER_NO_COST: - if (shop.getShopType() == ShopType.BITRADE && e.getAction() == Action.LEFT_CLICK_BLOCK) { - Message.INSUFFICIENT_ITEMS.sendMessage(buyer, new Tuple<>(Variable.ITEM.toString(), productName), new Tuple<>(Variable.AMOUNT.toString(), String.valueOf(amountProduct))); - } else { - Message.INSUFFICIENT_ITEMS.sendMessage(buyer, new Tuple<>(Variable.ITEM.toString(), costName), new Tuple<>(Variable.AMOUNT.toString(), String.valueOf(amountCost))); - } + Message.INSUFFICIENT_ITEMS.sendItemMultiLineMessage(buyer, Collections.singletonMap(Variable.MISSING_ITEMS, canExchangeResult.getRight())); return; case SHOP_NO_SPACE: - if (shop.getShopType() == ShopType.BITRADE && e.getAction() == Action.LEFT_CLICK_BLOCK) { - Message.SHOP_FULL.sendMessage(buyer, new Tuple<>(Variable.ITEM.toString(), productName), new Tuple<>(Variable.AMOUNT.toString(), String.valueOf(amountProduct))); - } else { - Message.SHOP_FULL.sendMessage(buyer, new Tuple<>(Variable.ITEM.toString(), costName), new Tuple<>(Variable.AMOUNT.toString(), String.valueOf(amountCost))); - } + Message.SHOP_FULL.sendMessage(buyer); return; case PLAYER_NO_SPACE: - if (shop.getShopType() == ShopType.BITRADE && e.getAction() == Action.LEFT_CLICK_BLOCK) { - Message.PLAYER_FULL.sendMessage(buyer, new Tuple<>(Variable.ITEM.toString(), costName), new Tuple<>(Variable.AMOUNT.toString(), String.valueOf(amountCost))); - } else { - Message.PLAYER_FULL.sendMessage(buyer, new Tuple<>(Variable.ITEM.toString(), productName), new Tuple<>(Variable.AMOUNT.toString(), String.valueOf(amountProduct))); - } + Message.PLAYER_FULL.sendMessage(buyer); return; case NOT_TRADE: e.setCancelled(false); @@ -197,49 +177,52 @@ public void onBlockInteract(PlayerInteractEvent e) { if (owner == null) owner = "-Unknown-"; - if (tradeAllItems(shop, multiplier, e, buyer)) { - if (amountCost != 0) { - Message.ON_TRADE.sendMessage(buyer, - new Tuple<>(Variable.AMOUNT.numbered(1), String.valueOf(amountProduct)), - new Tuple<>(Variable.AMOUNT.numbered(2), String.valueOf(amountCost)), - new Tuple<>(Variable.ITEM.numbered(1), productName.toLowerCase()), - new Tuple<>(Variable.ITEM.numbered(2), costName.toLowerCase()), - new Tuple<>(Variable.SELLER.toString(), shop.getShopType().isITrade() ? Setting.ITRADESHOP_OWNER.getString() : owner)); - } else { - Message.ON_TRADE.sendMessage(buyer, - new Tuple<>(Variable.AMOUNT.numbered(1), String.valueOf(amountProduct)), - new Tuple<>(Variable.AMOUNT.numbered(2) + " ", ""), //Also replaces the extra space - new Tuple<>(Variable.ITEM.numbered(1), productName.toLowerCase()), //1 - new Tuple<>(Variable.ITEM.numbered(2), Setting.ITRADESHOP_NO_COST_TEXT.getString()), - new Tuple<>(Variable.SELLER.toString(), shop.getShopType().isITrade() ? Setting.ITRADESHOP_OWNER.getString() : owner)); - } + Tuple, List> tradeReturn = tradeAllItems(shop, multiplier, e, buyer); + Map> tradedItems = new HashMap<>(); + + tradedItems.put(Variable.GIVEN_LINES, tradeReturn.getRight()); + tradedItems.put(Variable.RECEIVED_LINES, tradeReturn.getLeft()); + + if (tradeReturn.getLeft().get(0) != null && tradeReturn.getRight().get(0) != null) { + Message.ON_TRADE.sendItemMultiLineMessage(buyer, tradedItems, + new Tuple<>(Variable.SELLER.toString(), shop.getShopType().isITrade() ? Setting.ITRADESHOP_OWNER.getString() : owner)); + } else { + Message.FAILED_TRADE.sendMessage(buyer); } shop.updateSign(); shop.saveShop(); } - private boolean tradeAllItems(Shop shop, int multiplier, PlayerInteractEvent event, Player buyer) { + private Tuple, List> tradeAllItems(Shop shop, int multiplier, PlayerInteractEvent event, Player buyer) { Action action = event.getAction(); - List costItems = new ArrayList<>(), productItems = new ArrayList<>(); + List costItems = createBadList(), productItems = createBadList(); // Start with Bad lists so that in the event of failure at least one list has to have null at 0 Inventory shopInventory = shop.hasStorage() ? shop.getChestAsSC().getInventory() : null; Inventory playerInventory = buyer.getInventory(); if (shop.getShopType() == ShopType.ITRADE && action.equals(Action.RIGHT_CLICK_BLOCK)) { //ITrade trade if (!shop.getCost().isEmpty()) { - costItems = getItems(playerInventory, shop.getCost(), multiplier); + costItems = getItems(playerInventory.getStorageContents(), shop.getCost(), multiplier); if (costItems.get(0) == null) { ItemStack item = costItems.get(1); - Message.INSUFFICIENT_ITEMS.sendMessage(buyer, - new Tuple<>(Variable.ITEM.toString(), item.hasItemMeta() && item.getItemMeta().hasDisplayName() ? item.getItemMeta().getDisplayName() : item.getType().toString()), - new Tuple<>(Variable.AMOUNT.toString(), String.valueOf(item.getAmount() * multiplier))); - return false; + Message.INSUFFICIENT_ITEMS.sendItemMultiLineMessage(buyer, Collections.singletonMap(Variable.MISSING_ITEMS, costItems)); + return new Tuple<>(productItems, costItems); } for (ItemStack item : costItems) { playerInventory.removeItem(item); } + } else { + costItems.clear(); + + //Create Free item and set amount/name + ItemStack noCostITradeItem = new ItemStack(Material.STICK, Setting.ITRADESHOP_NO_COST_AMOUNT.getInt()); + ItemMeta noCostITradeMeta = noCostITradeItem.getItemMeta(); + noCostITradeMeta.setDisplayName(Setting.ITRADESHOP_NO_COST_TEXT.getString()); + noCostITradeItem.setItemMeta(noCostITradeMeta); + + costItems.add(noCostITradeItem); } Inventory iTradeVirtualInventory = Bukkit.createInventory(null, Math.min((int) (Math.ceil(shop.getProduct().size() / 9.0) * 9) * multiplier, 54)); @@ -250,7 +233,7 @@ private boolean tradeAllItems(Shop shop, int multiplier, PlayerInteractEvent eve } } - productItems = getItems(iTradeVirtualInventory, shop.getProduct(), multiplier); + productItems = getItems(iTradeVirtualInventory.getStorageContents(), shop.getProduct(), multiplier); for (ItemStack item : productItems) { playerInventory.addItem(item); @@ -258,50 +241,42 @@ private boolean tradeAllItems(Shop shop, int multiplier, PlayerInteractEvent eve Bukkit.getPluginManager().callEvent(new PlayerSuccessfulTradeEvent(buyer, costItems, productItems, shop, event.getClickedBlock(), event.getBlockFace())); PLUGIN.getMetricsManager().addTrade(); - return true; //Successfully completed trade + return new Tuple<>(productItems, costItems); //Successfully completed trade } else if (shop.getShopType() == ShopType.BITRADE && action == Action.LEFT_CLICK_BLOCK) { //BiTrade Reversed Trade //Method to find Cost items in player inventory and add to cost array - costItems = getItems(playerInventory, shop.getProduct(), multiplier); //Reverse BiTrade, Product is Cost + costItems = getItems(playerInventory.getStorageContents(), shop.getProduct(), multiplier); //Reverse BiTrade, Product is Cost if (costItems.get(0) == null) { ItemStack item = costItems.get(1); - Message.INSUFFICIENT_ITEMS.sendMessage(buyer, - new Tuple<>(Variable.ITEM.toString(), item.hasItemMeta() && item.getItemMeta().hasDisplayName() ? item.getItemMeta().getDisplayName() : item.getType().toString()), - new Tuple<>(Variable.AMOUNT.toString(), String.valueOf(item.getAmount() * multiplier))); - return false; + Message.INSUFFICIENT_ITEMS.sendItemMultiLineMessage(buyer, Collections.singletonMap(Variable.MISSING_ITEMS, costItems)); + return new Tuple<>(productItems, costItems); } //Method to find Product items in shop inventory and add to product array - productItems = getItems(shopInventory, shop.getCost(), multiplier); //Reverse BiTrade, Cost is Product + productItems = getItems(shopInventory.getStorageContents(), shop.getCost(), multiplier); //Reverse BiTrade, Cost is Product if (productItems.get(0) == null) { ItemStack item = productItems.get(1); shop.updateStatus(); - Message.SHOP_INSUFFICIENT_ITEMS.sendMessage(buyer, - new Tuple<>(Variable.ITEM.toString(), item.hasItemMeta() && item.getItemMeta().hasDisplayName() ? item.getItemMeta().getDisplayName() : item.getType().toString()), - new Tuple<>(Variable.AMOUNT.toString(), String.valueOf(item.getAmount() * multiplier))); - return false; + Message.SHOP_INSUFFICIENT_ITEMS.sendItemMultiLineMessage(buyer, Collections.singletonMap(Variable.MISSING_ITEMS, productItems)); + return new Tuple<>(productItems, costItems); } } else if (action.equals(Action.RIGHT_CLICK_BLOCK)) { // Normal Trade //Method to find Cost items in player inventory and add to cost array - costItems = getItems(playerInventory, shop.getCost(), multiplier); + costItems = getItems(playerInventory.getStorageContents(), shop.getCost(), multiplier); if (costItems.get(0) == null) { ItemStack item = costItems.get(1); - Message.INSUFFICIENT_ITEMS.sendMessage(buyer, - new Tuple<>(Variable.ITEM.toString(), item.hasItemMeta() && item.getItemMeta().hasDisplayName() ? item.getItemMeta().getDisplayName() : item.getType().toString()), - new Tuple<>(Variable.AMOUNT.toString(), String.valueOf(item.getAmount() * multiplier))); - return false; + Message.INSUFFICIENT_ITEMS.sendItemMultiLineMessage(buyer, Collections.singletonMap(Variable.MISSING_ITEMS, costItems)); + return new Tuple<>(productItems, costItems); } //Method to find Product items in shop inventory and add to product array - productItems = getItems(shopInventory, shop.getProduct(), multiplier); + productItems = getItems(shopInventory.getStorageContents(), shop.getProduct(), multiplier); if (productItems.get(0) == null) { ItemStack item = productItems.get(1); shop.updateStatus(); - Message.SHOP_INSUFFICIENT_ITEMS.sendMessage(buyer, - new Tuple<>(Variable.ITEM.toString(), item.hasItemMeta() && item.getItemMeta().hasDisplayName() ? item.getItemMeta().getDisplayName() : item.getType().toString()), - new Tuple<>(Variable.AMOUNT.toString(), String.valueOf(item.getAmount() * multiplier))); - return false; + Message.SHOP_INSUFFICIENT_ITEMS.sendItemMultiLineMessage(buyer, Collections.singletonMap(Variable.MISSING_ITEMS, productItems)); + return new Tuple<>(productItems, costItems); } } @@ -329,9 +304,9 @@ private boolean tradeAllItems(Shop shop, int multiplier, PlayerInteractEvent eve Bukkit.getPluginManager().callEvent(new PlayerSuccessfulTradeEvent(buyer, costItems, productItems, shop, event.getClickedBlock(), event.getBlockFace())); PLUGIN.getMetricsManager().addTrade(); - return true; //Successfully completed trade + return new Tuple<>(productItems, costItems); //Successfully completed trade } else { - return false; + return new Tuple<>(productItems, costItems); } } } \ No newline at end of file diff --git a/src/main/java/org/shanerx/tradeshop/objects/Shop.java b/src/main/java/org/shanerx/tradeshop/objects/Shop.java index 968b3423..87dbb070 100644 --- a/src/main/java/org/shanerx/tradeshop/objects/Shop.java +++ b/src/main/java/org/shanerx/tradeshop/objects/Shop.java @@ -348,7 +348,6 @@ public boolean removeUser(UUID oldUser) { } saveShop(); - updateSign(); return ret; } @@ -710,7 +709,6 @@ public void saveShop() { updateFullTradeCount(); utils.PLUGIN.getDataStorage().saveShop(this); updateUserFiles(); - updateSign(); } /** @@ -882,14 +880,14 @@ public ShopStatus setOpen() { * Checks if the shop has sufficient product stock to make a trade */ public boolean hasProductStock() { - return !shopType.isITrade() && hasProduct() && getChestAsSC() != null && getChestAsSC().hasStock(product); + return !shopType.isITrade() && hasProduct() && getChestAsSC() != null && getChestAsSC().hasStock(getProduct()); } /** * Checks if the shop has sufficient cost stock to make a trade(Use for BiTrade) */ public boolean hasCostStock() { - return !shopType.isITrade() && hasCost() && getChestAsSC() != null && getChestAsSC().hasStock(cost); + return !shopType.isITrade() && hasCost() && getChestAsSC() != null && getChestAsSC().hasStock(getCost()); } /** @@ -1003,12 +1001,7 @@ public List getUserNames() { * @return true if all costs are valid */ public boolean areCostsValid() { - for (ShopItemStack iS : cost) { - if (utils.isIllegal(IllegalItemList.TradeItemType.COST, iS.getItemStack().getType())) - return false; - } - - return true; + return areItemsValid(IllegalItemList.TradeItemType.COST, cost); } /** @@ -1017,8 +1010,19 @@ public boolean areCostsValid() { * @return true if all products are valid */ public boolean areProductsValid() { - for (ShopItemStack iS : product) { - if (utils.isIllegal(IllegalItemList.TradeItemType.PRODUCT, iS.getItemStack().getType())) + return areItemsValid(IllegalItemList.TradeItemType.PRODUCT, product); + } + + /** + * Checks if all Items in the list are valid for trade + * + * @param tradeItemType Should Illegal Items be checked as Costs or Products + * @param items List to check + * @return true if all products are valid + */ + private boolean areItemsValid(IllegalItemList.TradeItemType tradeItemType, List items) { + for (ShopItemStack iS : items) { + if (utils.isIllegal(tradeItemType, iS.getItemStack().getType())) return false; } diff --git a/src/main/java/org/shanerx/tradeshop/objects/ShopChest.java b/src/main/java/org/shanerx/tradeshop/objects/ShopChest.java index d9a9aa03..7ffc367d 100644 --- a/src/main/java/org/shanerx/tradeshop/objects/ShopChest.java +++ b/src/main/java/org/shanerx/tradeshop/objects/ShopChest.java @@ -207,7 +207,7 @@ public Inventory getInventory() { } public boolean hasStock(List itemToCheck) { - return itemToCheck.size() > 0 && getItems(getInventory(), itemToCheck, 1).get(0) != null; + return itemToCheck.size() > 0 && getItems(getInventory().getStorageContents(), itemToCheck, 1).get(0) != null; } public void loadFromName() { diff --git a/src/main/java/org/shanerx/tradeshop/objects/ShopItemStack.java b/src/main/java/org/shanerx/tradeshop/objects/ShopItemStack.java index 974903ce..e77fb81a 100644 --- a/src/main/java/org/shanerx/tradeshop/objects/ShopItemStack.java +++ b/src/main/java/org/shanerx/tradeshop/objects/ShopItemStack.java @@ -29,6 +29,7 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; import net.md_5.bungee.api.ChatColor; +import org.apache.commons.lang.WordUtils; import org.bukkit.FireworkEffect; import org.bukkit.Material; import org.bukkit.block.ShulkerBox; @@ -494,10 +495,19 @@ public String getItemName() { itemStack.getType().toString(); } - public String getCleanItemName() { + static public String getCleanItemName(ItemStack itemStack) { + BookMeta bookmeta = itemStack.hasItemMeta() && itemStack.getItemMeta() instanceof BookMeta ? ((BookMeta) itemStack.getItemMeta()) : null; + + if (bookmeta != null) + return bookmeta.getTitle(); + return ChatColor.stripColor(itemStack.hasItemMeta() && itemStack.getItemMeta().hasDisplayName() ? itemStack.getItemMeta().getDisplayName() : - itemStack.getType().toString()); + WordUtils.capitalizeFully(itemStack.getType().toString().replace("_", " "))); + } + + public String getCleanItemName() { + return ShopItemStack.getCleanItemName(itemStack); } public String serialize() { diff --git a/src/main/java/org/shanerx/tradeshop/utils/ObjectHolder.java b/src/main/java/org/shanerx/tradeshop/utils/ObjectHolder.java index 3212b9dd..5fef81db 100644 --- a/src/main/java/org/shanerx/tradeshop/utils/ObjectHolder.java +++ b/src/main/java/org/shanerx/tradeshop/utils/ObjectHolder.java @@ -39,8 +39,6 @@ public Type getObject() { @Override public String toString() { - return "ObjectHolder{" + - "obj=" + obj + - '}'; + return obj.toString(); } } diff --git a/src/main/java/org/shanerx/tradeshop/utils/Utils.java b/src/main/java/org/shanerx/tradeshop/utils/Utils.java index c3f81687..01d293d3 100644 --- a/src/main/java/org/shanerx/tradeshop/utils/Utils.java +++ b/src/main/java/org/shanerx/tradeshop/utils/Utils.java @@ -355,47 +355,56 @@ public boolean checkShopChest(Block sign) { * @return true if shop has enough cost to make trade */ public Boolean checkInventory(Inventory inv, List itemList, int multiplier) { - return getItems(inv, itemList, multiplier).get(0) != null; - } - - + return getItems(inv.getStorageContents(), itemList, multiplier).get(0) != null; + } + + /** + * Returns a 'bad' list with index 0 being `null` + * + * @return list with index 0 being `null` + */ + public List createBadList() { + List badList = new ArrayList<>(); + badList.add(null); + return badList; + } + /** * Checks whether a trade can take place. * - * @param shop the Shop object the player is trading with - * @param playerInv the Inventory object representing the inventory that is subject to the transaction. + * @param shop the Shop object the player is trading with + * @param playerInv the storageContents of the player inventory that is subject to the transaction. * @param multiplier the multiplier for the trade * @param action the action from the event * @return Exchange status with appropriate response */ - public ExchangeStatus canExchangeAll(Shop shop, Inventory playerInv, int multiplier, Action action) { + public Tuple> canExchangeAll(Shop shop, Inventory playerInv, int multiplier, Action action) { + if (shop.getShopType() != ShopType.BITRADE && action == Action.LEFT_CLICK_BLOCK) { + return new Tuple<>(ExchangeStatus.NOT_TRADE, createBadList()); + } + Inventory playerInventory = Bukkit.createInventory(null, playerInv.getStorageContents().length); playerInventory.setContents(playerInv.getStorageContents().clone()); - if (shop.getShopType() != ShopType.BITRADE && action == Action.LEFT_CLICK_BLOCK) { - return ExchangeStatus.NOT_TRADE; - } - - Inventory shopInv = null; - Inventory shopInventory = null; + Inventory shopInventory = null; - if (shop.getShopType() != ShopType.ITRADE) { - shopInv = shop.getChestAsSC().getInventory(); - shopInventory = Bukkit.createInventory(null, shopInv.getStorageContents().length); - shopInventory.setContents(shopInv.getStorageContents().clone()); - } + if (shop.getShopType() != ShopType.ITRADE) { + Inventory shopInv = shop.getChestAsSC().getInventory(); + shopInventory = Bukkit.createInventory(null, shopInv.getStorageContents().length); + shopInventory.setContents(shopInv.getStorageContents().clone()); + } List costItems, productItems; - if (shop.getShopType() == ShopType.ITRADE) { //ITrade trade + if (shop.getShopType() == ShopType.ITRADE) { //ITrade trade //Method to find Cost items in player inventory and add to cost array - costItems = getItems(playerInventory, shop.getCost(), multiplier); + costItems = getItems(playerInventory.getStorageContents(), shop.getCost(), multiplier); if (!costItems.isEmpty()) { if (costItems.get(0) == null) { - return ExchangeStatus.PLAYER_NO_COST; + return new Tuple<>(ExchangeStatus.PLAYER_NO_COST, costItems); } for (ItemStack item : costItems) { @@ -411,42 +420,42 @@ public ExchangeStatus canExchangeAll(Shop shop, Inventory playerInv, int multipl } } - productItems = getItems(iTradeVirtualInventory, shop.getProduct(), multiplier); + productItems = getItems(iTradeVirtualInventory.getStorageContents(), shop.getProduct(), multiplier); for (ItemStack item : productItems) { if (!playerInventory.addItem(item).isEmpty()) { - return ExchangeStatus.PLAYER_NO_SPACE; + return new Tuple<>(ExchangeStatus.PLAYER_NO_SPACE, createBadList()); } } - return ExchangeStatus.SUCCESS; //Successfully completed trade + return new Tuple<>(ExchangeStatus.SUCCESS, createBadList()); //Successfully completed trade } else if (shop.getShopType() == ShopType.BITRADE && action == Action.LEFT_CLICK_BLOCK) { //BiTrade Reversed Trade //Method to find Cost items in player inventory and add to cost array - costItems = getItems(playerInventory, shop.getProduct(), multiplier); //Reverse BiTrade, Product is Cost - if (costItems.get(0) == null) { - return ExchangeStatus.PLAYER_NO_COST; + costItems = getItems(playerInventory.getStorageContents(), shop.getProduct(), multiplier); //Reverse BiTrade, Product is Cost + if (costItems.get(0) == null) { + return new Tuple<>(ExchangeStatus.PLAYER_NO_COST, costItems); } //Method to find Product items in shop inventory and add to product array - productItems = getItems(shopInventory, shop.getCost(), multiplier); //Reverse BiTrade, Cost is Product + productItems = getItems(shopInventory.getStorageContents(), shop.getCost(), multiplier); //Reverse BiTrade, Cost is Product if (productItems.get(0) == null) { - shop.updateStatus(); - return ExchangeStatus.SHOP_NO_PRODUCT; + shop.updateStatus(); + return new Tuple<>(ExchangeStatus.SHOP_NO_PRODUCT, productItems); } } else { // Normal Trade //Method to find Cost items in player inventory and add to cost array - costItems = getItems(playerInventory, shop.getCost(), multiplier); - if (costItems.get(0) == null) { - return ExchangeStatus.PLAYER_NO_COST; + costItems = getItems(playerInventory.getStorageContents(), shop.getCost(), multiplier); + if (costItems.get(0) == null) { + return new Tuple<>(ExchangeStatus.PLAYER_NO_COST, costItems); } //Method to find Product items in shop inventory and add to product array - productItems = getItems(shopInventory, shop.getProduct(), multiplier); + productItems = getItems(shopInventory.getStorageContents(), shop.getProduct(), multiplier); if (productItems.get(0) == null) { - shop.updateStatus(); - return ExchangeStatus.SHOP_NO_PRODUCT; + shop.updateStatus(); + return new Tuple<>(ExchangeStatus.SHOP_NO_PRODUCT, productItems); } } @@ -464,30 +473,35 @@ public ExchangeStatus canExchangeAll(Shop shop, Inventory playerInv, int multipl //For loop to put cost items in shop inventory for (ItemStack item : costItems) { if (!shopInventory.addItem(item).isEmpty()) { - return ExchangeStatus.SHOP_NO_SPACE; - } - } + return new Tuple<>(ExchangeStatus.SHOP_NO_SPACE, createBadList()); + } + } - //For loop to put product items in player inventory - for (ItemStack item : productItems) { - if (!playerInventory.addItem(item).isEmpty()) { - return ExchangeStatus.PLAYER_NO_SPACE; - } - } + //For loop to put product items in player inventory + for (ItemStack item : productItems) { + if (!playerInventory.addItem(item).isEmpty()) { + return new Tuple<>(ExchangeStatus.PLAYER_NO_SPACE, createBadList()); + } + } - return ExchangeStatus.SUCCESS; //Successfully completed trade + return new Tuple<>(ExchangeStatus.SUCCESS, createBadList()); //Successfully completed trade } - //Returns an arraylist of the itemstacks to be removed/added, if it could not get enough of an item, will return index 0 as null and index 1 as item it could not get enough of - public List getItems(Inventory inventory, List search, int multiplier) { + /** + * Returns an arraylist of the ItemStack Objects to be removed/added, if it could not get enough of any items, it will return index 0 as null, followed by ItemStack Objects that could not be retrieved + * + * @param storageContents the storage contents of the inventory being checked + * @param search List of ShopItemStack Objects that need to be retrieved + * @param multiplier the multiplier for the trade + * @return List of ItemStack Objects to pull from inventory or if not enough; index 0 as null, followed by ItemStack Objects that could not be retrieved + */ + public List getItems(ItemStack[] storageContents, List search, int multiplier) { Map storage = new HashMap<>(), found = new HashMap<>(); - ArrayList good = new ArrayList(), bad = new ArrayList(); - - debugger.log("ShopTradeListener > Search List: " + search, DebugLevels.TRADE); + List good = new ArrayList(), bad = createBadList(); - bad.add(null); + debugger.log("Utils > getItems > Search List: " + search, DebugLevels.TRADE); - for (ItemStack itemStack : inventory.getStorageContents()) { + for (ItemStack itemStack : storageContents.clone()) { if (itemStack != null) { ItemStack tempItemStack = itemStack.clone(); int amount = tempItemStack.getAmount(); @@ -498,7 +512,7 @@ public List getItems(Inventory inventory, List search, } } - debugger.log("ShopTradeListener > Storage List: " + storage, DebugLevels.TRADE); + debugger.log("Utils > getItems > Storage List: " + storage, DebugLevels.TRADE); int totalCount = 0, currentCount = 0; @@ -534,9 +548,9 @@ public List getItems(Inventory inventory, List search, } } - debugger.log("ShopTradeListener > Good List: " + good, DebugLevels.TRADE); - debugger.log("ShopTradeListener > Bad List: " + bad, DebugLevels.TRADE); - debugger.log("ShopTradeListener > Return Status: " + (currentCount != totalCount ? "bad" : "good"), DebugLevels.TRADE); + debugger.log("Utils > getItems > Good List: " + good, DebugLevels.TRADE); + debugger.log("Utils > getItems > Bad List: " + bad, DebugLevels.TRADE); + debugger.log("Utils > getItems > Return Status: " + (currentCount != totalCount ? "bad" : "good"), DebugLevels.TRADE); return currentCount != totalCount ? bad : good; } diff --git a/src/main/java/org/shanerx/tradeshop/utils/config/Message.java b/src/main/java/org/shanerx/tradeshop/utils/config/Message.java index 920c02eb..238848bb 100644 --- a/src/main/java/org/shanerx/tradeshop/utils/config/Message.java +++ b/src/main/java/org/shanerx/tradeshop/utils/config/Message.java @@ -28,12 +28,20 @@ import org.bukkit.Bukkit; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; import org.shanerx.tradeshop.TradeShop; +import org.shanerx.tradeshop.objects.Debug; +import org.shanerx.tradeshop.objects.ShopItemStack; import org.shanerx.tradeshop.utils.Tuple; +import org.shanerx.tradeshop.utils.Utils; import org.yaml.snakeyaml.Yaml; import java.util.Arrays; +import java.util.List; +import java.util.Map; import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; public enum Message { @@ -90,11 +98,12 @@ public enum Message { VARIOUS_ITEM_TYPE(MessageSection.NONE, "various-item-type"), TOGGLED_STATUS(MessageSection.NONE, "toggled-status"), NO_SIGN_FOUND(MessageSection.NONE, "no-sign-found"), - ADMIN_TOGGLED(MessageSection.NONE, "admin-toggled"); + ADMIN_TOGGLED(MessageSection.NONE, "admin-toggled"), + FAILED_TRADE(MessageSection.NONE, "failed-trade"); public static final TradeShop PLUGIN = Objects.requireNonNull((TradeShop) Bukkit.getPluginManager().getPlugin("TradeShop")); - private final String key, path; + private final String key, path, MULTILINEREGEX = "[{](&V&=)[^}]*[}]"; private final MessageSection section; Message(MessageSection section, String key) { @@ -108,7 +117,7 @@ static void upgrade() { double version = MESSAGE_VERSION.getDouble(); ConfigManager configManager = PLUGIN.getMessageManager(); - //Changes if CONFIG_VERSION is below 1.1, then sets config version to 1.1 + //Changes if CONFIG_VERSION is below 1.1, then update to 1.1 if (version < 1.1) { if (TOO_MANY_ITEMS.getString().equals("&cThis trade can not take any more %side%!")) { TOO_MANY_ITEMS.setValue(PLUGIN.getLanguage().getDefault(Language.LangSection.MESSAGE, TOO_MANY_ITEMS.getPath())); @@ -116,7 +125,7 @@ static void upgrade() { version = 1.1; } - //Changes if CONFIG_VERSION is below 1.2, then sets config version to 1.2 + //Changes if CONFIG_VERSION is below 1.2, then update to 1.2 if (version < 1.2) { Arrays.stream(values()).forEach((message) -> { String str = message.getString().replace("{", "%").replace("}", "%"); @@ -128,6 +137,22 @@ static void upgrade() { version = 1.2; } + //Changes if CONFIG_VERSION is below 1.3, then update to 1.3 + if (version < 1.3) { + if (INSUFFICIENT_ITEMS.getString().equals("&cYou do not have &e%AMOUNT% %ITEM%&c!")) { + INSUFFICIENT_ITEMS.setValue(PLUGIN.getLanguage().getDefault(Language.LangSection.MESSAGE, INSUFFICIENT_ITEMS.getPath())); + } + if (SHOP_INSUFFICIENT_ITEMS.getString().equals("&cThis shop does not have enough &e%AMOUNT% %ITEM%&c to trade!")) { + SHOP_INSUFFICIENT_ITEMS.setValue(PLUGIN.getLanguage().getDefault(Language.LangSection.MESSAGE, SHOP_INSUFFICIENT_ITEMS.getPath())); + } + if (ON_TRADE.getString().equals("&aYou have traded your &e%AMOUNT2% %ITEM2% &afor &e%AMOUNT1% %ITEM1% &awith %SELLER%")) { + ON_TRADE.setValue(PLUGIN.getLanguage().getDefault(Language.LangSection.MESSAGE, ON_TRADE.getPath())); + } + + + version = 1.3; + } + MESSAGE_VERSION.setValue(version); } @@ -240,6 +265,53 @@ public final void sendMessage(Player player, Tuple... replacemen } } + @SafeVarargs + public final void sendItemMultiLineMessage(Player player, Map> itemsToFill, Tuple... replacements) { + if (itemsToFill.isEmpty()) { + sendMessage(player, replacements); + return; + } + + boolean isJson = getString().startsWith("#json "); + String message = getPrefixed().replaceFirst("#json ", ""); + + Debug debug = new Utils().debugger; + + for (Map.Entry> entry : itemsToFill.entrySet()) { + Pattern pattern = Pattern.compile(MULTILINEREGEX.replace("&V&", entry.getKey().toString())); + Matcher matcher = pattern.matcher(message); + + if (entry.getValue().get(0) == null) { + entry.getValue().remove(0); + } + + while (matcher.find()) { + StringBuilder itemList = new StringBuilder(); + String found = matcher.group(), format = found.replaceAll("[{}]", "").split("=")[1]; + + for (ItemStack itm : entry.getValue()) { + itemList.append("\n") + .append(format.replace(Variable.ITEM.toString(), ShopItemStack.getCleanItemName(itm)) + .replace(Variable.AMOUNT.toString(), itm.getAmount() + "")); + } + + message = message.replace(found, itemList.toString()); + } + } + + for (Tuple replace : replacements) { + message = message.replace(replace.getLeft().toUpperCase(), replace.getRight()) + .replace(replace.getLeft().toLowerCase(), replace.getRight()) + .replace(replace.getLeft(), replace.getRight()); + } + + if (isJson) { + sendMessageDirectJson(player, message); + } else { + sendMessageDirect(player, message); + } + } + @SafeVarargs public final void sendMessage(CommandSender sender, Tuple... replacements) { if (sender instanceof Player) { diff --git a/src/main/java/org/shanerx/tradeshop/utils/config/Setting.java b/src/main/java/org/shanerx/tradeshop/utils/config/Setting.java index 6deba996..fb9613f7 100644 --- a/src/main/java/org/shanerx/tradeshop/utils/config/Setting.java +++ b/src/main/java/org/shanerx/tradeshop/utils/config/Setting.java @@ -100,6 +100,7 @@ public enum Setting { ITRADESHOP_HEADER(SettingSection.ITRADE_SHOP_OPTIONS, "header", "iTrade"), ITRADESHOP_EXPLODE(SettingSection.ITRADE_SHOP_OPTIONS, "allow-explode", false), ITRADESHOP_NO_COST_TEXT(SettingSection.ITRADE_SHOP_OPTIONS, "no-cost-text", "nothing"), + ITRADESHOP_NO_COST_AMOUNT(SettingSection.ITRADE_SHOP_OPTIONS, "no-cost-amount", "1"), // BiTrade Shop Options BITRADESHOP_HEADER(SettingSection.BITRADE_SHOP_OPTIONS, "header", "BiTrade"), diff --git a/src/main/java/org/shanerx/tradeshop/utils/config/Variable.java b/src/main/java/org/shanerx/tradeshop/utils/config/Variable.java index 20d35197..0c32a0ce 100644 --- a/src/main/java/org/shanerx/tradeshop/utils/config/Variable.java +++ b/src/main/java/org/shanerx/tradeshop/utils/config/Variable.java @@ -41,7 +41,10 @@ public enum Variable { PLAYER, LEVEL, STATUS, - STATE; + STATE, + MISSING_ITEMS, + RECEIVED_LINES, + GIVEN_LINES; final String SURROUND = "%"; diff --git a/src/main/resources/Lang/en-us.yml b/src/main/resources/Lang/en-us.yml index c4a33e2f..f2ba39d0 100644 --- a/src/main/resources/Lang/en-us.yml +++ b/src/main/resources/Lang/en-us.yml @@ -145,6 +145,8 @@ setting: pre-comment: "Can explosions damage the shop sign (true/false)" no-cost-text: pre-comment: "What text should be used for successful trades when no cost is present" + no-cost-amount: + pre-comment: "What amount should be used for successful trades when no cost is present(must be greater than 0)" post-comment: "\n" bitrade-shop-options: header: @@ -185,10 +187,10 @@ message: default: "&cYou cannot add a Shulker Box as a cost when the shop uses it for storage." pre-comment: "Text to display when a shop failed creation due to using a shulker box as cost when the shop uses it for storage: " insufficient-items: - default: "&cYou do not have &e%AMOUNT% %ITEM%&c!" + default: "&cYou are missing the below items for the trade! \n{%MISSINGITEMS%= &e%AMOUNT% %ITEM%}" pre-comment: "Text to display when the player does not have enough items:" shop-insufficient-items: - default: "&cThis shop does not have enough &e%AMOUNT% %ITEM%&c to trade!" + default: "&cThis shop is missing the below items for the trade! \n{%MISSINGITEMS%= &e%AMOUNT% %ITEM%}" pre-comment: "Text to display when the shop does not have enough items:" invalid-arguments: default: "&eTry &6/tradeshop help &eto display help!" @@ -235,7 +237,7 @@ message: default: "&cThat TradeShop does not belong to you" pre-comment: "Text to display when a player attempts to open a shop they do not own nor have been granted access to:" on-trade: - default: "&aYou have traded your &e%AMOUNT2% %ITEM2% &afor &e%AMOUNT1% %ITEM1% &awith %SELLER%" + default: "&aYou have traded with %SELLER% receiving: \n{%RECEIVEDLINES%= &e%AMOUNT% %ITEM%} \n&aIn exchange for: \n{%GIVENLINES%= &e%AMOUNT% %ITEM%}" pre-comment: "Text to display upon a successful trade:" player-full: default: "&cYour inventory is full, please make room before trading items!" @@ -299,4 +301,7 @@ message: admin-toggled: default: "&aYour Admin mode is currently &e%STATE%&a." pre-comment: "Text to display when an admin toggles or views their Admin abilities. \n# \"%STATE%\" will be replaced by the state that the player is in after the command." + failed-trade: + default: "&cThe Trade has failed for unknown reasons, please notify an Admin or submit a bug report using '/ts bug'!" + pre-comment: "Text to display when a player is too far from a sign" \ No newline at end of file