diff --git a/.classpath b/.classpath deleted file mode 100644 index 60c8e56..0000000 --- a/.classpath +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/.project b/.project deleted file mode 100644 index 80cf29a..0000000 --- a/.project +++ /dev/null @@ -1,17 +0,0 @@ - - - TradeCraft - - - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.eclipse.jdt.core.javanature - - diff --git a/TradeCraft.jar b/TradeCraft.jar deleted file mode 100644 index 7f9a454..0000000 Binary files a/TradeCraft.jar and /dev/null differ diff --git a/TradeCraft.properties b/TradeCraft.properties deleted file mode 100644 index 75eb5dd..0000000 --- a/TradeCraft.properties +++ /dev/null @@ -1,11 +0,0 @@ -infinite-shops-enabled = true -player-owned-shops-enabled = true -repair-shops-enabled = true -group-required-to-create-infinite-shops = * -group-required-to-create-player-owned-shops = * -group-required-to-create-repair-shops = * -group-required-to-buy-from-shops = * -group-required-to-sell-to-shops = * -group-required-to-use-repair-shops = * -repair-cost = 10 -enable-debug-messages = true \ No newline at end of file diff --git a/com/mjmr89/TradeCraft/TradeCraft.java b/com/mjmr89/TradeCraft/TradeCraft.java deleted file mode 100644 index 8ea4cb1..0000000 --- a/com/mjmr89/TradeCraft/TradeCraft.java +++ /dev/null @@ -1,341 +0,0 @@ -package com.mjmr89.TradeCraft; - -import java.io.File; -import java.util.ArrayList; -import java.util.logging.Logger; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.bukkit.Material; -import org.bukkit.Server; -import org.bukkit.block.Block; -import org.bukkit.block.Chest; -import org.bukkit.block.Sign; -import org.bukkit.command.Command; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; -import org.bukkit.event.Listener; -import org.bukkit.event.Event.Priority; -import org.bukkit.event.Event.Type; -import org.bukkit.plugin.Plugin; -import org.bukkit.plugin.PluginDescriptionFile; -import org.bukkit.plugin.PluginLoader; -import org.bukkit.plugin.PluginManager; -import org.bukkit.plugin.java.JavaPlugin; - -import com.nijiko.permissions.PermissionHandler; -import com.nijikokun.bukkit.Permissions.Permissions; - -public class TradeCraft extends JavaPlugin { - - // The plugin name. - static final String pluginName = "TradeCraft"; - - private static final Pattern ratePattern = Pattern - .compile("\\s*(\\d+)\\s*:\\s*(\\d+)\\s*"); - - // Stuff used to interact with the server. - final Logger log = Logger.getLogger("Minecraft"); - final Server server = this.getServer(); - - // Objects used by the plugin. - static Material currency; - TradeCraftPropertiesFile properties = new TradeCraftPropertiesFile(); - TradeCraftConfigurationFile configuration = new TradeCraftConfigurationFile( - this); - TradeCraftDataFile data = new TradeCraftDataFile(this); - - private final TradeCraftBlockListener blockListener = new TradeCraftBlockListener( - this); - private final TradeCraftPlayerListener playerListener = new TradeCraftPlayerListener( - this); - public TradeCraftPermissions permissions = new TradeCraftPermissions(this); - public Permissions permissionsPlugin = null; - public boolean permEnabled = false; - - public void onDisable() { - } - - public void onEnable() { - - properties = new TradeCraftPropertiesFile(); - configuration = new TradeCraftConfigurationFile(this); - data = new TradeCraftDataFile(this); - - currency = Material.getMaterial(properties.getCurrencyTypeId()); - configuration.load(); - data.load(); - permissions.setupPermissions(); - - PluginManager pm = this.getServer().getPluginManager(); - - pm.registerEvent(Type.BLOCK_RIGHTCLICKED, blockListener, - Priority.Normal, this); - - pm - .registerEvent(Type.SIGN_CHANGE, blockListener, - Priority.Normal, this); - pm - .registerEvent(Type.BLOCK_BREAK, blockListener, - Priority.Normal, this); - - PluginDescriptionFile pdfFile = this.getDescription(); - System.out.println(pdfFile.getName() + " version " - + pdfFile.getVersion() + " is enabled!"); - - } - - @Override - public boolean onCommand(CommandSender sender, Command command, - String label, String[] args) { - String name = command.getName(); - Player p; - - if (sender instanceof Player) { - p = (Player) sender; - - if (name.equalsIgnoreCase("setcurrency") && args.length == 1) { - try { - int cid = Integer.parseInt(args[0]); - currency = Material.getMaterial(cid); - p.sendMessage("Currency is set to " + currency); - } catch (NumberFormatException nfe) { - Material m = Material.getMaterial(args[0]); - if (m != null) { - currency = m; - p.sendMessage("Currency is set to " + currency); - - } - } finally { - return true; - } - } else if (name.equalsIgnoreCase("displaycurrency") - && args.length == 0) { - p.sendMessage("Currency is: " + currency); - } else if (name.equalsIgnoreCase("canplayer") && args.length == 1) { - permissions.debug(args[0]); - } else if (name.equalsIgnoreCase("myshops")) { - displayShops(p); - } - - } else { - return false; - } - - return true; - } - - void displayShops(Player p) { - String name = p.getName(); - ArrayList list = data.shopsOwned(name); - if (list.size() == 0) { - p.sendMessage("You don't own any shops!"); - return; - } - p.sendMessage("Your shops:"); - for (TradeCraftDataInfo info : list) { - - p.sendMessage("Item: " + Material.getMaterial(info.itemType) - + " Amount: " + info.itemAmount + "Gold: " - + info.currencyAmount); - - } - - } - - void sendMessage(Player player, String format, Object... args) { - String message = String.format(format, args); - player.sendMessage(message); - } - - void trace(Player player, String format, Object... args) { - if (properties.getEnableDebugMessages()) { - sendMessage(player, format, args); - } - } - - TradeCraftShop getShopFromSignOrChestBlock(Player player, Block block) { - if (block.getType() == Material.CHEST) { - block = player.getWorld().getBlockAt(block.getX(), - block.getY() + 1, block.getZ()); - } - - return getShopFromSignBlock(player, block); - } - - TradeCraftShop getShopFromSignBlock(Player player, Block block) { - if (block.getType() != Material.WALL_SIGN) { - return null; - } - - int x = block.getX(); - int y = block.getY(); - int z = block.getZ(); - - trace(player, "You clicked a sign at %d, %d, %d.", x, y, z); - - Sign sign = (Sign) player.getWorld().getBlockAt(x, y, z).getState(); - - // The sign at this location can be null if it was just destroyed. - if (sign == null) { - trace(player, "The sign is no longer there."); - - return null; - } - - String itemName = getItemName(sign); - - if (itemName == null) { - trace(player, "There is no item name on the sign."); - - return null; - } - - trace(player, "The item name on the sign is %s.", itemName); - - Block blockBelowSign = player.getWorld().getBlockAt(x, y - 1, z); - - if (blockBelowSign.getType() != Material.CHEST) { - trace(player, "There is no chest beneath the sign."); - return null; - } - - Chest chest = (Chest) player.getWorld().getBlockAt(x, y - 1, z) - .getState(); - - if (itemName.toLowerCase().equals("repair")) { - if (!properties.getRepairShopsEnabled()) { - trace(player, "Repair shops are not enabled."); - return null; - } - - if (!player.isOp()) { - trace(player, "You can't use repair shops."); - return null; - } - - trace(player, "This is a repair shop."); - return new TradeCraftRepairShop(this, sign, chest); - } - - if (!configuration.isConfigured(itemName)) { - trace(player, - "The item name %s is not configured in the config file.", - itemName); - return null; - } - - String ownerName = getOwnerName(sign); - - if (ownerName == null) { - trace(player, "There is no owner name on the sign."); - - if (!properties.getInfiniteShopsEnabled()) { - trace(player, "Ininite shops are not enabled."); - return null; - } - - trace(player, "This is an infinite shop."); - return new TradeCraftInfiniteShop(this, sign, chest); - } - - trace(player, "The owner name on the sign is %s.", ownerName); - - if (!properties.getPlayerOwnedShopsEnabled()) { - trace(player, "Player-owned shops are not enabled."); - return null; - } - - trace(player, "This is a player-owned shop."); - return new TradeCraftPlayerOwnedShop(this, sign, chest, ownerName); - } - - String getItemName(Sign sign) { - return getSpecialText(sign, "[", "]"); - } - - String getOwnerName(Sign sign) { - return getSpecialTextOnLine(sign, "-", "-", 3); - } - - private String getSpecialText(Sign sign, String prefix, String suffix) { - for (int i = 0; i < 4; i++) { - String text = getSpecialTextOnLine(sign, prefix, suffix, i); - - if (text != null) { - return text; - } - } - - return null; - } - - private String getSpecialTextOnLine(Sign sign, String prefix, - String suffix, int lineNumber) { - String signText = sign.getLine(lineNumber); - - if (signText == null) { - return null; - } - - signText = signText.trim(); - - if (signText.startsWith(prefix) && signText.endsWith(suffix) - && signText.length() > 2) { - - String text = signText.substring(1, signText.length() - 1); - text = text.trim(); - - if (text.equals("")) { - return null; - } - - return text; - } - - return null; - } - - TradeCraftExchangeRate getExchangeRate(Sign sign, int lineNumber) { - TradeCraftExchangeRate rate = new TradeCraftExchangeRate(); - - String signText = sign.getLine(lineNumber); - - Matcher matcher = ratePattern.matcher(signText); - - if (matcher.find()) { - rate.amount = Integer.parseInt(matcher.group(1)); - rate.value = Integer.parseInt(matcher.group(2)); - } - - return rate; - } - - static int getMaxStackSize(int itemType) { - /* - * switch (Material.getMaterial(itemType)) { case APPLE: case - * Golden_Apple: case Pork: case Grilled_Pork: case Bread: case Bucket: - * case Water_Bucket: case Lava_Bucket: case Milk_Bucket: case - * Wood_Sword: case Wood_Spade: case Wood_Pickaxe: case Wood_Axe: case - * Wood_Hoe: case Stone_Sword: case Stone_Spade: case Stone_Pickaxe: - * case Stone_Axe: case Stone_Hoe: case Iron_Sword: case Iron_Spade: - * case Iron_Pickaxe: case Iron_Axe: case Iron_Hoe: case Diamond_Sword: - * case Diamond_Spade: case Diamond_Pickaxe: case Diamond_Axe: case - * Diamond_Hoe: case Gold_Sword: case Gold_Spade: case Gold_Pickaxe: - * case Gold_Axe: case Gold_Hoe: case Leather_Helmet: case - * Leather_Chestplate: case Leather_Leggings: case Leather_Boots: case - * Iron_Helmet: case Iron_Chestplate: case Iron_Leggings: case - * Iron_Boots: case Diamond_Helmet: case Diamond_Chestplate: case - * Diamond_Leggings: case Diamond_Boots: case Gold_Helmet: case - * Gold_Chestplate: case Gold_Leggings: case Gold_Boots: return 1; case - * SnowBall: return 16; } return 64; - */ - return 64; - } - - public void onLoad() { - // TODO Auto-generated method stub - - } - -} \ No newline at end of file diff --git a/com/mjmr89/TradeCraft/TradeCraftBlockListener.java b/com/mjmr89/TradeCraft/TradeCraftBlockListener.java deleted file mode 100644 index ca4ac9b..0000000 --- a/com/mjmr89/TradeCraft/TradeCraftBlockListener.java +++ /dev/null @@ -1,130 +0,0 @@ -package com.mjmr89.TradeCraft; - -import org.bukkit.Material; -import org.bukkit.block.Block; -import org.bukkit.block.Chest; -import org.bukkit.block.Sign; -import org.bukkit.entity.Player; -import org.bukkit.event.block.BlockBreakEvent; -import org.bukkit.event.block.BlockListener; -import org.bukkit.event.block.BlockRightClickEvent; -import org.bukkit.event.block.SignChangeEvent; -import org.bukkit.inventory.Inventory; - - -public class TradeCraftBlockListener extends BlockListener{ - - private TradeCraft plugin; - - TradeCraftBlockListener(TradeCraft plugin){ - this.plugin = plugin; - } - - public void debug(String str){ - plugin.getServer().broadcastMessage(str); - } - - @Override - public void onBlockRightClick(BlockRightClickEvent e){ - Block blockClicked = e.getBlock(); - Player player = e.getPlayer(); - - TradeCraftShop shop = plugin.getShopFromSignBlock(player, blockClicked); - - if (shop == null) { - return; - } - - shop.handleRightClick(player); - } - - @Override - public void onBlockBreak(BlockBreakEvent e) { - Player player = e.getPlayer(); - Block block = e.getBlock(); - - TradeCraftShop shop = plugin.getShopFromSignOrChestBlock(player, block); - - if (shop == null) { - - return; - } - - if (shop.playerCanDestroy(player) || plugin.permissions.canDestroyShops(player)) { - if (!shop.shopCanBeWithdrawnFrom()) { - plugin.data.deleteShop(shop); - return; - } - - plugin.sendMessage(player, "All items and gold must be withdrawn before you can destroy this sign or chest!"); - - stopDestruction(block,e); - return; - } - - plugin.sendMessage(player, "You can't destroy this sign or chest!"); - - stopDestruction(block,e); - return; - } - - public void stopDestruction(Block b, BlockBreakEvent e){ - if(b.getState() instanceof Sign){ - Sign sign = (Sign)b.getState(); - String[] lines = sign.getLines(); - e.setCancelled(true); - for(int i = 0;i<4;i++){ - sign.setLine(i, lines[i]); - } - - sign.update(true); - return; - }else if(b.getState() instanceof Chest){ - e.setCancelled(true); - } - - } - - public void onSignChange(SignChangeEvent e) { - Player player = e.getPlayer(); - Sign sign = (Sign) e.getBlock().getState(); - - String ownerName = plugin.getOwnerName(sign); - - if (ownerName == null) { - String itemName = plugin.getItemName(sign); - - if (itemName == null) { - - return; - } - - if (plugin.permissions.canMakeInfShops(player)){ - - return; - } - - plugin.sendMessage(player, "You can't create infinite shops!"); - - - return; - } - - if ( plugin.permissions.canMakePlayerShops(player)){ - - return; - } - - if (player.getName().startsWith(ownerName)) { - plugin.data.setOwnerOfSign(player.getName(), sign); - - return; - } - - plugin.sendMessage(player, "You can't create signs with other players names on them!"); - - - return; - } - -} diff --git a/com/mjmr89/TradeCraft/TradeCraftConfigurationFile.java b/com/mjmr89/TradeCraft/TradeCraftConfigurationFile.java deleted file mode 100644 index ac9702c..0000000 --- a/com/mjmr89/TradeCraft/TradeCraftConfigurationFile.java +++ /dev/null @@ -1,99 +0,0 @@ -package com.mjmr89.TradeCraft; - -import java.io.BufferedReader; -import java.io.FileReader; -import java.io.IOException; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -class TradeCraftConfigurationFile { - - private static final String fileName = TradeCraft.pluginName + ".txt"; - - private static final Pattern commentPattern = Pattern.compile("^\\s*#.*$"); - private static final Pattern infoPattern = Pattern.compile( - "^\\s*([^,]+)\\s*," + // name - "\\s*(\\d+)\\s*" + // id - "(?:,\\s*(\\d+)\\s*:\\s*(\\d+))?\\s*" + // buyAmount:buyValue - "(?:,\\s*(\\d+)\\s*:\\s*(\\d+))?\\s*$"); // sellAmount:sellValue - - private final TradeCraft plugin; - private final Map infos = new HashMap(); - - TradeCraftConfigurationFile(TradeCraft plugin) { - this.plugin = plugin; - } - - void load() { - try { - infos.clear(); - - BufferedReader configurationFile = new BufferedReader(new FileReader(fileName)); - - int lineNumber = 0; - String line; - - while ((line = configurationFile.readLine()) != null) { - lineNumber += 1; - - if (line.trim().equals("")) { - continue; - } - - Matcher commentMatcher = commentPattern.matcher(line); - - if (commentMatcher.matches()) { - continue; - } - - Matcher infoMatcher = infoPattern.matcher(line); - - if (!infoMatcher.matches()) { - plugin.log.warning( - "Failed to parse line number " + lineNumber + - " in " + fileName + - ": " + line); - continue; - } - - TradeCraftConfigurationInfo info = new TradeCraftConfigurationInfo(); - info.name = infoMatcher.group(1); - info.id = Integer.parseInt(infoMatcher.group(2)); - - if (infoMatcher.group(3) != null) { - info.sellAmount = info.buyAmount = Integer.parseInt(infoMatcher.group(3)); - info.sellValue = info.buyValue = Integer.parseInt(infoMatcher.group(4)); - } - - if (infoMatcher.group(5) != null) { - info.sellAmount = Integer.parseInt(infoMatcher.group(5)); - info.sellValue = Integer.parseInt(infoMatcher.group(6)); - } - - infos.put(info.name.toUpperCase(), info); - } - plugin.log.info("Loaded " + infos.size() + " configs"); - - configurationFile.close(); - } catch (IOException e) { - plugin.log.warning("Error reading " + fileName); - } - } - - public String[] getNames() { - String[] names = infos.keySet().toArray(new String[0]); - Arrays.sort(names); - return names; - } - - public boolean isConfigured(String name) { - return infos.containsKey(name.toUpperCase()); - } - - public TradeCraftConfigurationInfo get(String name) { - return infos.get(name.toUpperCase()); - } -} \ No newline at end of file diff --git a/com/mjmr89/TradeCraft/TradeCraftConfigurationInfo.java b/com/mjmr89/TradeCraft/TradeCraftConfigurationInfo.java deleted file mode 100644 index a2c8d58..0000000 --- a/com/mjmr89/TradeCraft/TradeCraftConfigurationInfo.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.mjmr89.TradeCraft; - -class TradeCraftConfigurationInfo { - public String name; - public int id; - public int buyAmount; - public int buyValue; - public int sellAmount; - public int sellValue; -} \ No newline at end of file diff --git a/com/mjmr89/TradeCraft/TradeCraftDataFile.java b/com/mjmr89/TradeCraft/TradeCraftDataFile.java deleted file mode 100644 index feccb34..0000000 --- a/com/mjmr89/TradeCraft/TradeCraftDataFile.java +++ /dev/null @@ -1,277 +0,0 @@ -package com.mjmr89.TradeCraft; - -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.bukkit.Location; -import org.bukkit.block.Sign; - -class TradeCraftDataFile { - - private static final String fileName = TradeCraft.pluginName + ".data"; - private static final Pattern infoPattern1 = Pattern.compile( - "^\\s*(-?\\d+)\\s*,\\s*(-?\\d+)\\s*,\\s*(-?\\d+)" + // x,y,z - "\\s*=\\s*" + - "(\\d+)\\s*,\\s*(\\d+)\\s*$"); // itemAmount,currencyAmount - private static final Pattern infoPattern2 = Pattern.compile( - "^\\s*([^,]+)\\s*," + // ownerName - "\\s*(-?\\d+)\\s*,\\s*(-?\\d+)\\s*,\\s*(-?\\d+)\\s*," + // x,y,z - "\\s*(\\d+)\\s*," + // itemType - "\\s*(\\d+)\\s*," + // itemAmount - "\\s*(\\d+)\\s*$"); // currencyAmount - - private final TradeCraft plugin; - private final Map data = new HashMap(); - private final File dFile = new File(fileName); - - - TradeCraftDataFile(TradeCraft plugin) { - this.plugin = plugin; - } - - public void load() { -// if (!dFile.exists()) { -// plugin.log.info("No " + fileName + " file to read. Creating one now."); -// try { -// dFile.createNewFile(); -// } catch (IOException e) { -// // TODO Auto-generated catch block -// e.printStackTrace(); -// } -// return; -// } - - try { - dFile.createNewFile(); - data.clear(); - - BufferedReader reader = new BufferedReader(new FileReader(fileName)); - - String line; - int lineNumber = 0; - - while ((line = reader.readLine()) != null) { - lineNumber += 1; - - Matcher infoMatcher2 = infoPattern2.matcher(line); - - if (infoMatcher2.matches()) { - String ownerName = infoMatcher2.group(1); - int x = Integer.parseInt(infoMatcher2.group(2)); - int y = Integer.parseInt(infoMatcher2.group(3)); - int z = Integer.parseInt(infoMatcher2.group(4)); - int itemType = Integer.parseInt(infoMatcher2.group(5)); - int itemAmount = Integer.parseInt(infoMatcher2.group(6)); - int currencyAmount = Integer.parseInt(infoMatcher2.group(7)); - - String key = getKey(x, y, z); - - TradeCraftDataInfo info = new TradeCraftDataInfo(); - info.ownerName = ownerName; - info.itemType = itemType; - info.itemAmount = itemAmount; - info.currencyAmount = currencyAmount; - - data.put(key, info); - } else { - Matcher infoMatcher1 = infoPattern1.matcher(line); - - if (!infoMatcher1.matches()) { - plugin.log.warning( - "Failed to parse line number " + lineNumber + - " in " + fileName + - ": " + line); - continue; - } - - int x = Integer.parseInt(infoMatcher1.group(1)); - int y = Integer.parseInt(infoMatcher1.group(2)); - int z = Integer.parseInt(infoMatcher1.group(3)); - int itemAmount = Integer.parseInt(infoMatcher1.group(4)); - int currencyAmount = Integer.parseInt(infoMatcher1.group(5)); - - String key = getKey(x, y, z); - - TradeCraftDataInfo info = new TradeCraftDataInfo(); - info.ownerName = "unknown"; - info.itemAmount = itemAmount; - info.currencyAmount = currencyAmount; - - data.put(key, info); - } - } - - plugin.log.info("Loaded " + data.size() + " shops"); - reader.close(); - } catch (IOException e) { - plugin.log.warning("Error reading " + fileName); - } - } - - public void save() { - try { - BufferedWriter writer = new BufferedWriter(new FileWriter(fileName)); - - for (String key : data.keySet()) { - TradeCraftDataInfo info = data.get(key); - writer.write(info.ownerName + "," + - key + "," + - info.itemType + "," + - info.itemAmount + "," + - info.currencyAmount); - writer.newLine(); -// plugin.getServer().broadcastMessage(info.ownerName + "," + -// key + "," + -// info.itemType + "," + -// info.itemAmount + "," + -// info.currencyAmount); - } - - writer.close(); - } catch (IOException e) { - plugin.log.warning("Error writing " + fileName); - } - } - - public void deleteShop(TradeCraftShop shop){ - Location l = shop.sign.getBlock().getLocation(); - String key = getKey(l.getBlockX(),l.getBlockY(),l.getBlockZ()); - if(data.containsKey(key)){ - data.remove(key); - save(); - } - } - - public ArrayList shopsOwned(String name){ - ArrayList list = new ArrayList(); - - for (String key : data.keySet()) { - TradeCraftDataInfo info = data.get(key); - if(info.ownerName.equalsIgnoreCase(name)){ - list.add(info); - } - } - - - return list; - } - - public void setOwnerOfSign(String ownerName, Sign sign) { - depositCurrency(ownerName, sign, 0); - } - - public String getOwnerOfSign(Sign sign) { - String key = getKeyFromSign(sign); - if (data.containsKey(key)) { - TradeCraftDataInfo info = data.get(key); - return info.ownerName; - } - return null; - } - - public int getItemAmount(Sign sign) { - String key = getKeyFromSign(sign); - if (data.containsKey(key)) { - TradeCraftDataInfo info = data.get(key); - return info.itemAmount; - } - return 0; - } - - public int getCurrencyAmount(Sign sign) { - String key = getKeyFromSign(sign); - if (data.containsKey(key)) { - TradeCraftDataInfo info = data.get(key); - return info.currencyAmount; - } - return 0; - } - - public void depositItems(String ownerName, Sign sign, int itemType, int itemAmount) { - String key = getKeyFromSign(sign); - if (data.containsKey(key)) { - TradeCraftDataInfo info = data.get(key); - info.ownerName = ownerName; // For old entries that don't have the name. - info.itemType = itemType; // For old entries that don't have the type. - info.itemAmount += itemAmount; - } else { - TradeCraftDataInfo info = new TradeCraftDataInfo(); - info.ownerName = ownerName; - info.itemType = itemType; - info.itemAmount = itemAmount; - data.put(key, info); - } - save(); - } - - public void depositCurrency(String ownerName, Sign sign, int currencyAmount) { - String key = getKeyFromSign(sign); - if (data.containsKey(key)) { - TradeCraftDataInfo info = data.get(key); - info.ownerName = ownerName; // For old entries that don't have the name. - info.currencyAmount += currencyAmount; - } else { - TradeCraftDataInfo info = new TradeCraftDataInfo(); - info.ownerName = ownerName; - info.currencyAmount = currencyAmount; - data.put(key, info); - } - save(); - } - - public int withdrawItems(Sign sign) { - String key = getKeyFromSign(sign); - if (!data.containsKey(key)) { - return 0; - } - TradeCraftDataInfo info = data.get(key); - int itemAmount = info.itemAmount; - if (itemAmount != 0) { - info.itemAmount = 0; - save(); - } - return itemAmount; - } - - public int withdrawCurrency(Sign sign) { - String key = getKeyFromSign(sign); - if (!data.containsKey(key)) { - return 0; - } - TradeCraftDataInfo info = data.get(key); - int currencyAmount = info.currencyAmount; - if (currencyAmount != 0) { - info.currencyAmount = 0; - save(); - } - return currencyAmount; - } - - public void updateItemAndCurrencyAmounts(Sign sign, int itemAdjustment, int currencyAdjustment) { - String key = getKeyFromSign(sign); - if (!data.containsKey(key)) { - return; - } - TradeCraftDataInfo info = data.get(key); - info.itemAmount += itemAdjustment; - info.currencyAmount += currencyAdjustment; - save(); - } - - private String getKeyFromSign(Sign sign) { - return getKey(sign.getX(), sign.getY(), sign.getZ()); - } - - private String getKey(int x, int y, int z) { - return x + "," + y + "," + z; - } -} \ No newline at end of file diff --git a/com/mjmr89/TradeCraft/TradeCraftDataInfo.java b/com/mjmr89/TradeCraft/TradeCraftDataInfo.java deleted file mode 100644 index 1d2aba8..0000000 --- a/com/mjmr89/TradeCraft/TradeCraftDataInfo.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.mjmr89.TradeCraft; - -class TradeCraftDataInfo { - public String ownerName; - public int itemType; - public int itemAmount; - public int currencyAmount; -} \ No newline at end of file diff --git a/com/mjmr89/TradeCraft/TradeCraftExchangeRate.java b/com/mjmr89/TradeCraft/TradeCraftExchangeRate.java deleted file mode 100644 index d019bef..0000000 --- a/com/mjmr89/TradeCraft/TradeCraftExchangeRate.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.mjmr89.TradeCraft; - -public class TradeCraftExchangeRate { - public int amount; - public int value; -} diff --git a/com/mjmr89/TradeCraft/TradeCraftItemShop.java b/com/mjmr89/TradeCraft/TradeCraftItemShop.java deleted file mode 100644 index 6736564..0000000 --- a/com/mjmr89/TradeCraft/TradeCraftItemShop.java +++ /dev/null @@ -1,241 +0,0 @@ -package com.mjmr89.TradeCraft; - -import org.bukkit.Material; -import org.bukkit.block.Chest; -import org.bukkit.block.Sign; -import org.bukkit.entity.Player; - -public abstract class TradeCraftItemShop extends TradeCraftShop { - - public TradeCraftItemShop(TradeCraft plugin, Sign sign, Chest chest) { - super(plugin, sign, chest); - } - - public void handleRightClick(Player player) { - if (isOwnedByPlayer(player)) { - handleOwnerClick(player); - } else { - handlePatronClick(player); - } - } - - private void handleOwnerClick(Player player) { - if (!chestContentsAreOK()) { - plugin.sendMessage(player, "The chest has more than one type of item in it!"); - return; - } - - if (getChestItemCount() == 0) { - int goldAmount = withdrawCurrency(); - if (goldAmount > 0) { - populateChest(TradeCraft.currency.getId(), goldAmount); - plugin.sendMessage(player, "Withdrew %1$d gold.", goldAmount); - } else { - int itemAmount = withdrawItems(); - if (itemAmount > 0) { - populateChest(getItemType(), itemAmount); - plugin.sendMessage(player, "Withdrew %1$d %2$s.", itemAmount, getItemName()); - } else { - plugin.sendMessage(player, "There is nothing to withdraw."); - } - } - } else if (getChestItemType() == TradeCraft.currency.getId()) { - depositCurrency(getChestItemCount()); - plugin.sendMessage(player, "Deposited %1$d gold.", getChestItemCount()); - populateChest(0, 0); - int itemAmount = withdrawItems(); - if (itemAmount > 0) { - populateChest(getItemType(), itemAmount); - plugin.sendMessage(player, "Withdrew %1$d %2$s.", itemAmount, getItemName()); - } - } else if (getChestItemType() == getItemType()) { - depositItems(getChestItemCount()); - populateChest(0, 0); - plugin.sendMessage(player, "Deposited %1$d %2$s.", getChestItemCount(), getItemName()); - } else { - plugin.sendMessage(player, "You can't deposit that here!"); - } - } - - private void handlePatronClick(Player player) { - - - boolean playerCanBuy= (plugin.permissions.canBuy(player)); - boolean playerCanSell = plugin.permissions.canSell(player); - - getChestItemCount(); - - if (!chestContentsAreOK()) { - plugin.sendMessage(player, "The chest has more than one type of item in it!"); - return; - } - - if (getChestItemCount() == 0) { - if (playerCanBuy && playerCanBuy()) { - plugin.sendMessage(player, - "You can buy %1$d %2$s for %3$d gold.", - getBuyAmount(), - getItemName(), - getBuyValue()); - } - - if (playerCanSell && playerCanSell()) { - plugin.sendMessage(player, - "You can sell %1$d %2$s for %3$d gold.", - getSellAmount(), - getItemName(), - getSellValue()); - } - - plugin.sendMessage(player, "The chest is empty."); - return; - } - - - - if (getChestItemType() == TradeCraft.currency.getId()) { - if (!playerCanBuy) { - plugin.sendMessage(player, "You are not allowed to buy from shops!"); - } else { - playerWantsToBuy(player); - } - } else if (getChestItemType() == getItemType()) { - if (!playerCanSell) { - plugin.sendMessage(player, "You are not allowed to sell to shops!"); - } else { - playerWantsToSell(player); - } - } else { - plugin.sendMessage(player, "You can't sell that here!"); - } - } - - private void playerWantsToBuy(Player player) { - if (!playerCanBuy()) { - plugin.sendMessage(player, "You can't buy that here!"); - return; - } - - int currencyPlayerWantsToSpend = getChestItemCount(); - int amountPlayerWantsToBuy = currencyPlayerWantsToSpend * getBuyAmount() / getBuyValue(); - - if (amountPlayerWantsToBuy == 0) { - plugin.sendMessage(player, - "You need to spend at least %1$d gold to get any %2$s.", - getBuyValue(), - getItemName()); - return; - } - - if (amountPlayerWantsToBuy > getItemsInShop()) { - plugin.sendMessage(player, - "Cannot buy. This shop only has %1$d %2$s.", - getItemsInShop(), - getItemName()); - return; - } - - int requiredGoldForThatAmount = amountPlayerWantsToBuy * getBuyValue() / getBuyAmount(); - - updateItemAndCurrencyAmounts(-amountPlayerWantsToBuy, requiredGoldForThatAmount); - - chest.clear(); - chest.add(TradeCraft.currency.getId(), currencyPlayerWantsToSpend - requiredGoldForThatAmount); - chest.add(getItemType(), amountPlayerWantsToBuy); - chest.update(); - - plugin.sendMessage(player, - "You bought %1$d %2$s for %3$d gold.", - amountPlayerWantsToBuy, - getItemName(), - requiredGoldForThatAmount); - } - - private void playerWantsToSell(Player player) { - if (!playerCanSell()) { - plugin.sendMessage(player, "You can't sell that here!"); - return; - } - - int amountPlayerWantsToSell = getChestItemCount(); - int currencyPlayerShouldReceive = amountPlayerWantsToSell * getSellValue() / getSellAmount(); - - if (currencyPlayerShouldReceive == 0) { - plugin.sendMessage(player, - "You need to sell at least %1$d %2$s to get any gold.", - getSellAmount(), - getItemName()); - return; - } - - if (currencyPlayerShouldReceive > getCurrencyInShop()) { - plugin.sendMessage(player, - "Cannot sell. This shop only has %1$d gold.", - getCurrencyInShop()); - return; - } - - int amountThatCanBeSold = currencyPlayerShouldReceive * getSellAmount() / getSellValue(); - - updateItemAndCurrencyAmounts(amountThatCanBeSold, -currencyPlayerShouldReceive); - - chest.clear(); - chest.add(getItemType(), amountPlayerWantsToSell - amountThatCanBeSold); - chest.add(TradeCraft.currency.getId(), currencyPlayerShouldReceive); - chest.update(); - - plugin.sendMessage(player, - "You sold %1$d %2$s for %3$d gold.", - amountThatCanBeSold, - getItemName(), - currencyPlayerShouldReceive); - } - - public int getChestItemType() { - return chest.id; - } - - public int getChestItemCount() { - return chest.total; - } - - public boolean chestContentsAreOK() { - return chest.containsOnlyOneItemType(); - } - - public void populateChest(int id, int amount) { - chest.populateChest(id, amount); - } - - public abstract boolean isOwnedByPlayer(Player player); - - public abstract int getItemType(); - - public abstract String getItemName(); - - public abstract boolean playerCanBuy(); - - public abstract boolean playerCanSell(); - - public abstract int getBuyAmount(); - - public abstract int getBuyValue(); - - public abstract int getSellAmount(); - - public abstract int getSellValue(); - - public abstract int getItemsInShop(); - - public abstract int getCurrencyInShop(); - - public abstract void depositItems(int amount); - - public abstract void depositCurrency(int amount); - - public abstract int withdrawItems(); - - public abstract int withdrawCurrency(); - - public abstract void updateItemAndCurrencyAmounts(int itemAdjustment, int CurrencyAdjustment); -} \ No newline at end of file diff --git a/com/mjmr89/TradeCraft/TradeCraftPermissions.java b/com/mjmr89/TradeCraft/TradeCraftPermissions.java deleted file mode 100644 index e8c701e..0000000 --- a/com/mjmr89/TradeCraft/TradeCraftPermissions.java +++ /dev/null @@ -1,83 +0,0 @@ -package com.mjmr89.TradeCraft; - -import org.bukkit.entity.Player; -import org.bukkit.plugin.Plugin; - -import com.nijiko.permissions.PermissionHandler; -import com.nijikokun.bukkit.Permissions.Permissions; - -public class TradeCraftPermissions { - - PermissionHandler permHandler = null; - TradeCraft plugin; - - TradeCraftPermissions(TradeCraft plugin) { - this.plugin = plugin; - } - - public void setupPermissions() { - Plugin test = plugin.getServer().getPluginManager().getPlugin("Permissions"); - - if (permHandler == null) { - if (test != null) { - this.permHandler = ((Permissions)test).getHandler(); - plugin.permEnabled = true; - System.out.println("[TradeCraft] has recognized Permissions"); - } - } - } - - public boolean canBuy(Player p){ - if(plugin.permEnabled == true){ - return permHandler.has(p, "TradeCraft.canBuy"); - }else - return true; - } - - public boolean canSell(Player p){ - if(plugin.permEnabled == true){ - return permHandler.has(p, "TradeCraft.canSell"); - }else - return true; - } - - public boolean canMakeInfShops(Player p){ - if(plugin.permEnabled == true){ - return permHandler.has(p, "TradeCraft.canMakeInfShops"); - }else - return p.isOp(); - } - - public boolean canMakePlayerShops(Player p){ - if(plugin.permEnabled == true){ - return permHandler.has(p, "TradeCraft.canMakePlayerShops"); - }else - return p.isOp(); - } - - public boolean canDestroyShops(Player p){ - if(plugin.permEnabled == true){ - return permHandler.has(p, "TradeCraft.canDestroyShops"); - }else - return p.isOp(); - } - - public void debug(String n){ - Player p = plugin.getServer().getPlayer(n); - String name = p.getName(); - if(p == null){ - plugin.getServer().broadcastMessage("/canPlayer used with a name of player who is not online."); - return; - } - - plugin.log.info("" + name + " has:"); - plugin.log.info("canbuy " + canBuy(p)); - plugin.log.info("cansell " + canSell(p)); - plugin.log.info("canmakeinf " + canMakeInfShops(p)); - plugin.log.info("canmakepersonal " + canMakePlayerShops(p)); - plugin.log.info("candestroy " + canDestroyShops(p)); - - - } - -} diff --git a/com/mjmr89/TradeCraft/TradeCraftPlayerListener.java b/com/mjmr89/TradeCraft/TradeCraftPlayerListener.java deleted file mode 100644 index 474464b..0000000 --- a/com/mjmr89/TradeCraft/TradeCraftPlayerListener.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.mjmr89.TradeCraft; - -import org.bukkit.entity.Player; -import org.bukkit.event.player.PlayerChatEvent; -import org.bukkit.event.player.PlayerListener; - - -public class TradeCraftPlayerListener extends PlayerListener{ - - private TradeCraft plugin; - - TradeCraftPlayerListener(TradeCraft plugin){ - this.plugin = plugin; - } - - private void displayItems(Player player) { - String[] names = plugin.configuration.getNames(); - StringBuilder sb = new StringBuilder(); - for (String name : names) { - if (sb.length() + name.length() > 60) { - plugin.sendMessage(player, sb.toString()); - sb = new StringBuilder(); - } - if (sb.length() > 0) { - sb.append(", "); - } - sb.append(name); - } - if (sb.length() > 0) { - plugin.sendMessage(player, sb.toString()); - } - } - - private void displaySecurity(Player player) { - plugin.permissions.debug(player.getName()); - } - -} diff --git a/com/mjmr89/TradeCraft/TradeCraftPropertiesFile.java b/com/mjmr89/TradeCraft/TradeCraftPropertiesFile.java deleted file mode 100644 index 1a5f3c3..0000000 --- a/com/mjmr89/TradeCraft/TradeCraftPropertiesFile.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.mjmr89.TradeCraft; - -import java.io.File; -import java.io.IOException; - -import org.bukkit.util.config.Configuration; - -public class TradeCraftPropertiesFile { - - private File f = new File(TradeCraft.pluginName + ".properties"); - private final Configuration properties; - - public TradeCraftPropertiesFile() { - - if(!f.exists()){ - try { - f.createNewFile(); -// populate(); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - - properties = new Configuration(f); - } - - public void populate(){ - properties.setProperty("infinite-shops-enabled",true); - - properties.save(); - } - - public int getCurrencyTypeId(){ - return properties.getInt("currency-id",266); - } - - public boolean getInfiniteShopsEnabled() { - return properties.getBoolean("infinite-shops-enabled", true); - } - - public boolean getPlayerOwnedShopsEnabled() { - return properties.getBoolean("player-owned-shops-enabled", true); - } - - public boolean getRepairShopsEnabled() { - return properties.getBoolean("repair-shops-enabled", false); - } - - public int getRepairCost() { - return properties.getInt("repair-cost", 0); - } - - public boolean getEnableDebugMessages() { - return properties.getBoolean("enable-debug-messages", false); - } -} diff --git a/com/mjmr89/TradeCraft/TradeCraftRepairShop.java b/com/mjmr89/TradeCraft/TradeCraftRepairShop.java deleted file mode 100644 index a148304..0000000 --- a/com/mjmr89/TradeCraft/TradeCraftRepairShop.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.mjmr89.TradeCraft; - -import java.util.List; - -import org.bukkit.Material; -import org.bukkit.block.Chest; -import org.bukkit.block.Sign; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; - -public class TradeCraftRepairShop extends TradeCraftShop { - - public TradeCraftRepairShop(TradeCraft plugin, Sign sign, Chest chest) { - super(plugin, sign, chest); - } - - public void handleRightClick(Player player) { - int gold = chest.getAmountOfCurrencyInChest(); - List items = chest.getNonCurrencyItems(); - int repairCost = plugin.properties.getRepairCost(); - - if (gold == 0 && items.size() == 0) { - plugin.sendMessage(player, "It costs %d gold to repair an item.", repairCost); - return; - } - - int actualCost = items.size() * repairCost; - - if (items.size() == 0) { - plugin.sendMessage(player, "With this much gold, you can repair %d items.", gold / repairCost); - return; - } - - if (gold < actualCost) { - if (gold > 0) { - plugin.sendMessage(player, "That's not enough gold."); - } - plugin.sendMessage(player, "You need %d gold to repair all this.", actualCost); - return; - } - - chest.clear(); - - for (ItemStack item : items) { - chest.add(item.getType().getId(), 1); - } - - chest.add(plugin.currency.getId(), (gold - actualCost)); - - chest.update(); - - plugin.sendMessage(player, "You repaired %d items for %d gold.", items.size(), actualCost); - } - - public boolean playerCanDestroy(Player player) { - return true; - } - - public boolean shopCanBeWithdrawnFrom() { - return false; - } -} diff --git a/plugin.yml b/plugin.yml deleted file mode 100644 index a86da38..0000000 --- a/plugin.yml +++ /dev/null @@ -1,16 +0,0 @@ -name: TradeCraft -main: com.mjmr89.TradeCraft.TradeCraft -version: 0.7 -commands: - myshops: - description: See a printout of any personal shops you have - usage: - setCurrency: - description: Set the currency used. - usage: Enter an id or a material name (using _ as spaces) - displayCurrency: - description: Check what currency is being used. - usage: Displays the current material name of the currency used. - canPlayer: - description: See what permissions a player has regarding TradeCraft - usage: enter a user name \ No newline at end of file diff --git a/README.txt b/src/README.txt similarity index 68% rename from README.txt rename to src/README.txt index b55d491..03191a8 100644 --- a/README.txt +++ b/src/README.txt @@ -1,8 +1,9 @@ TradeCraft ========== -This is a plugin for Bukkit that allows you to buy and sell items using gold -ingots (bars) as a currency. +This is a plugin for Bukkit that allows you to buy and sell items using any currency. + +See also: http://forums.bukkit.org/threads/econ-tradecraft-ae-gold-based-economy.13797/ Installation ============ @@ -10,18 +11,19 @@ Installation The plugin is installed like any other Bukkitplugin. Put the TradeCraft.jar file in your plugins folder. -The plugin is configured using a file called TradeCraft.txt that you need to -create in the same folder as your server.properties folder. Details on the -format of the file appear below. +The plugin is configured using a file called TradeCraft.txt that will automatically +be placed in the plugins/TradeCraft/ folder. Details on the format of the file +appear below. -There is also a file called TradeCraft.properties that you can put in the same -folder as your server.properties folder. You can set properties that configure -how the plugin works in this file. Look inside the example file that comes with -the plugin to see the properties you can set. +There is also a file called TradeCraft.properties in the same folder. You can set +properties that configure how the plugin works in this file. Look inside the +example file that comes with the plugin to see the properties you can set. Shops ===== +A manual with pictures here: http://dev.bukkit.org/server-mods/tradecraft-ae/pages/manual/ + Players buy and sell items at "shops". To create a shop, place a chest (single, not double) next to a wall. Place a @@ -61,14 +63,12 @@ Or, it could just say this: The item type has to be on a line all by itself and there can't be any spaces outside or inside the square brackets. Case isn't important. The item types are -defined by you in the TradeCraft.txt file. +defined by you in the TradeCraft.properties file. No state is maintained in infinite shops. Players can buy an infinite amount of items (assuming they have enough gold) from infinite shops. They can also sell an infinite amount of items (earning an infinite amount of gold). -TODO: Allow administrators to disable infinite shops using -TradeCraft.properties. Player-owned shops ================== @@ -83,8 +83,8 @@ order to keep it in operation. The format for the text on a player-owned shop must look something like this: [Sand] -Buy for 32:1 -Sell for 48:1 +Buy 32 for 1 +Sell 48 for 1 -injektilo- The first line is the type of item bought and sold at that shop. This has to @@ -104,11 +104,12 @@ be part of their name. For example, if the player's name was "NumberOneMinecraftFan", they could use "-NumberOne-" as the name on the sign. TODO: Allow administrators to set aliases or nicknames for players for use on -signs. +signs. Or have support for an existing alias plugin. Players are not allowed to create signs that contain other players' names. Likewise, players are not allowed to destroy signs (or the chests underneath -them) containing other players' names. +them, or the block behind then) containing other players' names. Also all items +have to be removed from player shops before they can be destroyed. Using shops =========== @@ -154,47 +155,64 @@ Configuration ============= To configure what can be traded and for how much (at infinite shops), you need -to edit TradeCraft.txt. The file should look like this: - -# Comments look like this. -Sand,12,32:1 -Diamond,264,1:64 +to edit items.yml. The file should look like this: + +Cobblestone: + itemTypeId: 4 + itemTypeData: 0 + buyAmount: 64 + buyValue: 1 + sellAmount: 64 + sellValue: 1 +Gravel: + itemTypeId: 13 + itemTypeData: 0 + buyAmount: 32 + buyValue: 1 + sellAmount: 32 + sellValue: 4 +Sand: + itemTypeId: 12 + itemTypeData: 0 + buyAmount: 32 + buyValue: 1 + sellAmount: 32 + sellValue: 1 +Dirt: + itemTypeId: 3 + itemTypeData: 0 + buyAmount: 32 + buyValue: 1 + sellAmount: 32 + sellValue: 1 +Coal: + itemTypeId: 263 + itemTypeData: 0 + buyAmount: 3 + buyValue: 1 + sellAmount: 4 + sellValue: 1 The first value is the name of the item as you want it to appear on your signs and in the messages the players see when they make their trades. -The second value is the block or item ID. You can see these values here: +The second value is the block or item ID, optionally with a data value added +(separated with a semicolon) for colored wool, logs, etc. You can see these +values here: http://www.minecraftwiki.net/wiki/Data_values -The third value is the exchange rate. The number before the colon is how many -of that item needs to be sold to earn the number of gold specified as the -number after the colon. In the above example, players have to sell 32 sand -blocks to get a single gold. They have to pay 64 gold to buy a single diamond. - -You don't have to use the number 1 on either side of the colon. For example, -you could use a ratio like 3:2. That means that selling 3 items will get you 2 -gold. Selling 6 items will get you 4 gold. Likewise, spending 2 gold will get -you 3 items and spending 4 gold will get you 6 items. +The values in buyAmount and buyValue is the amount of that item and the +amount of currency respectively when the player is buying. Likewise, the +values in sellAmount is the exchange rate when the player is selling. +In the above example, players have to sell 4 Coal to get 1 currency. +They have to pay 1 currency to buy a 3 Coal. It's possible to configure separate exchange rates for buying and selling. If only a single ratio is specified, that ratio is used for both buying and selling. If two ratios are specified, the first is for buying and the second is for selling. -For example, to let players buy 32 sand for 1 gold, but only be able to sell 64 -sand for 1 gold, you would configure it like this: - -Sand,12,32:1,64:1 - -If you use a ratio of 0:0, that disables buying or selling of that item type. - -For example, to allow players to buy diamonds, but not sell them, you would -configure it like this: - -Diamond,264,1:64,0:0 - -If you click on a sign above an empty chest, you'll see a message saying what -the exchange rates for both buying and selling are. This is only necessary for -infinite shops since player-owned shops have to display their exchange rates -on the sign. +If you right click on a sign above an empty chest, you'll see a message saying what +the exchange rates for both buying and selling are. On player shops you will +also get information about the amount of items and 'money' still in the shop. diff --git a/src/TODO.txt b/src/TODO.txt new file mode 100644 index 0000000..4e47c80 --- /dev/null +++ b/src/TODO.txt @@ -0,0 +1,34 @@ +- repairshop has it's own handling code / enable repair shop + + +Digi said: + - Also, could you add a feature to place signs ON chests (like Lockette) ? + - Protection of chest contents while you're using it... or something, so other people don't just sit by and wait for you to buy and steal your stuff. + -- Can be done by implementing features like VirtualChest has. This used CraftBukkit code to open a virtual inventory. + + - limit currency and configured items to valid data values + - Allow administrators to disable infinite shops using a command and storing that setting in TradeCraft.properties. + - warn when config files use old format? + + + - Customer right-clicking telling about rates and availability only display the amount of items. Could perhaps display "up to 12 Logs" instead of "up to 12". + + Coding issue: + - items with 'damage' bits too high will act as the basic item, but cannot be traded or stacked as such. Be careful not to create these. + -- The server will automatically stack items that do not have actually existing multiple version through the damage bit. + + Done: + - [launch player] triggers infinite shop check + - (gsand) Would it be possible to make it so players can buy 6 items for 2 gold, but not 3 for 1 gold. + - The wool, dye, log, etc sub-items (just re-mentioning, I understand it's gonna be possible in #564+) + - Additional information about current amount of items and currency available when right clicking as client. + - You don't have to write your name on the sign of a player shop anymore. Also, too long names will be stored just fine, but will show abbreviated on the sign. + - The sign labels would be better understood if they're: "Buy 5 for 1" and "Sell 5 for 1" and you should suppot texts like "Buy 5 for 1g" or "Buy5for1" etc. + - No message is shown when a customer places too few currency/items in the chest and right-clicks. + - Allow setting of currency by name, as defined in config. + +Food for thought, answered: + - What happens when a sign/chest is removed while the plugin is disabled .. / with the entry in the .data file later.. when a shop is placed in the exact same spot again? + -- A new shop (sign on placement) overwrites the old one in the collection, since that uses only coordinates as key. + - Messages after right-clicking use the item name as on the sign, not as in TradeCraft.txt (capitalization issue only). + -- Just part of how the plugin works. Could perhaps change it, but it's a minor issue. \ No newline at end of file diff --git a/src/TradeCraft.en.lang b/src/TradeCraft.en.lang new file mode 100644 index 0000000..466047b --- /dev/null +++ b/src/TradeCraft.en.lang @@ -0,0 +1,72 @@ +# TradeCraft.java +ERROR_IN_FORMAT_STRING: "There was an error in the format string:" +NO_PERMISSION_SET_CURRENCY: "You do not have the permission to set the currency" +IS_NO_VALID_CURRENCY_USE_INSTEAD: "%1$s is not a valid value for a currency, use 'id[;data]' or 'itemname'." +INVALID_CURRENCY: "Invalid currency: %1$s" +CURRENCY_IS_SET_TO_A_IDDATA: "Currency is set to: %1$s (%2$s)" +CURRENCY_IS_A_IDDATA: "Currency is: %1$s (%2$s)" +NO_SUCH_PLAYER: "There is no such player." +YOU_DONT_OWN_ANY_SHOPS: "You don't own any shops!" +A_DOES_NOT_OWN_ANY_SHOPS: "%1$s doesn't own any shops." +YOUR_SHOPS: "Your shops:" +SHOPS_OF_A: "Shops of %1$s:" +ITEM: "Item" +AMOUNT: "Amount" +POSSIBLE_COMMANDS_FOR_THE_PLUGIN: "Possible commands for the %1$s plugin:" +TC_HELP_THIS_TEXT: "this help text" +TC_SHOPS: "lists the shops the player owns" +TC_PSHOPS: "lists the shops the given player owns" +TC_CURRENCY_OPT_PARAM_GETSET_CURRENCY: "get and set the currency" +TC_CURRENCY_GET_CURRENCY: "get the current currency" +TC_CAN_PLAYER: "show the permissions of the given player" +TC_RELOAD: "reload the script and with that all the configuration files" +RESTARTING_PLUGIN: "Restarting %1$s" +RESTARTING_PLUGIN_DONE: "Restarting %1$s completed" + +# TradeCraftBlockListener.java +ALL_ITEMS_MUST_BE_WITHDRAWN: "All items and currency must be withdrawn before you can destroy this sign or chest!" +YOU_CANT_DESTROY_THIS_SIGN: "You can't destroy this sign!" +YOU_CANT_DESTROY_THIS_CHEST: "You can't destroy this chest!" +YOU_CANT_DESTROY_THIS_BLOCK_ATTACHED: "You can't destroy this block because there are signs attached to it!" +YOU_CANT_CREATE_INF_SHOPS: "You can't create infinite shops!" +YOU_DONT_HAVE_PERM_CREATE_PLAYER_SHOP: "You do not have the permission to create a player shop!" +TOTAL_SHOP_LIMIT_X: "You already have the maximum amount of shops (%1$d)!" +WORLD_SHOP_LIMIT_X: "You already have the maximum amount of shops in this world (%1$d)!" + +# TradeCraftItemShop.java +THE_CHEST_HAS_MORE_THAN_ONE_TYPE: "The chest has more than one type of item in it!" +WITHDREW_X_A: "Withdrew %1$d %2$s." +WITHDREW_X_CURRENCY_SHOP_STILL_HOLDS_Y_CURRENCY: "Withdrew the maximum %1$d %2$s, there is still %3$d %2$s in the shop." +WITHDREW_X_A_SHOP_STILL_HOLDS_Y_A: "Withdrew the maximum of %1$d %2$s, there are still %3$d %2$s in the shop." +THERE_IS_NOTHING_TO_WITHDRAW: "There is nothing to withdraw." +DEPOSITED_X_A: "Deposited %1$d %2$s." +YOU_CANT_DEPOSIT_THAT_HERE: "You can't deposit that here!" +YOU_CAN_BUY_Y_A_FOR_X_B: "You can buy %1$d %2$s for %3$d %4$s." +YOU_CAN_BUY_X_A_FOR_Y_B_UP_TO_Z: "You can buy %1$d %2$s for %3$d %4$s, up to %5$d." +YOU_CAN_SELL_X_A_FOR_Y_B: "You can sell %1$d %2$s for %3$d %4$s." +YOU_CAN_SELL_X_A_FOR_Y_B_UP_TO_Z: "You can sell %1$d %2$s for %3$d %4$s, up to %5$d %4$s." +THIS_IS_AN_INFINITE_SHOP: "This is an infinite shop" +YOU_ARE_NOT_ALLOWED_TO_BUY: "You are not allowed to buy from shops!" +YOU_ARE_NO_ALLOWED_TO_SELL: "You are not allowed to sell to shops!" +YOU_CANT_SELL_THAT: "You can't sell that here!" +YOU_CANT_BUY_HERE: "You can't buy here!" +YOU_NEED_TO_SPEND_AT_LEAST_X_A_TO_GET_ANY_B: "You need to spend at least %1$d %2$s to get any %3$s." +CANT_BUY_SHOP_HAS_NO_A_LEFT: "Cannot buy. This shop has no more %1$s left." +CANNOT_BUY_SHOP_ONLY_HAS_X_A: "Cannot buy. This shop only has %1$d %2$s." +YOU_NEED_TO_SELL_AT_LEAST_X_A_TO_GET_ANY_B: "You need to sell at least %1$d %2$s to get any %3$s." +CANNOT_SELL_SHOP_ONLY_HAS_X_A: "Cannot sell. This shop only has %1$d %2$s." +YOU_SOLD_X_A_FOR_Y_B: "You sold %1$d %2$s for %3$d %4$s." +SOLD_X_A_FOR_Y_B: "Sold %1$d %2$s for %3$d %4$s." +YOU_BOUGHT_X_A_FOR_Y_B: "You bought %1$d %2$s for %3$d %4$s." +BOUGHT_X_A_FOR_Y_B: "Bought %1$d %2$s for %3$d %4$s." +THIS_SHOP_ALWAYS_RETURNS_TOO_MUCH: "This shop would always return more goods than the chest could contain, contact the shop owner." +THIS_SHOP_WOULD_RETURN_TOO_MANY_BUY_LESS: "This shop would return more goods than the chest could contain, try to buy less." +THIS_SHOP_ALWAYS_RETURNS_TOO_MUCH_CURRENCY: "This shop would always return more %1$s than the chest could contain, contact the shop owner." +THIS_SHOP_WOULD_RETURN_TOO_MUCH_CURRENCY_SELL_LESS: "This shop would return more %1$s than the chest could contain, try to sell less items." + +# TradeCraftRepairShop.java +IT_COSTS_X_A_TO_REPAIR_AN_ITEM: "It costs %1$d %2$s to repair an item." +WITH_THIS_MUCH_A_YOU_CAN_REPAIR_Y_ITEMS: "With this much %1$s you can repair %2$d items." +THAT_IS_NOT_ENOUGH_A: "That's not enough %1$s." +YOU_NEED_X_A_TO_REPAIR_ALL_THIS: "You need %1$d %2$s to repair all this." +YOU_REPAIRED_X_ITEMS_FOR_Y_B: "You repaired %1$d items for %2$d %3$s." \ No newline at end of file diff --git a/src/TradeCraft.ge.lang b/src/TradeCraft.ge.lang new file mode 100644 index 0000000..53f37bd --- /dev/null +++ b/src/TradeCraft.ge.lang @@ -0,0 +1,68 @@ +# TradeCraft.java +ERROR_IN_FORMAT_STRING: "Fehler im Format string:" +NO_PERMISSION_SET_CURRENCY: "Du hast keine Berechtigung die Währung umzustellen" +IS_NO_VALID_CURRENCY_USE_INSTEAD: "%1$s ist keine gültige Währung, nutze 'id[;data]' oder 'itemname'." +INVALID_CURRENCY: "Falsche Währung: %1$s" +CURRENCY_IS_SET_TO_A_IDDATA: "Währung umgestellt zu: %1$s (%2$s)" +CURRENCY_IS_A_IDDATA: "Währung ist: %1$s (%2$s)" +NO_SUCH_PLAYER: "Kein gültiger Spieler." +YOU_DONT_OWN_ANY_SHOPS: "Du hast keine Shops!" +A_DOES_NOT_OWN_ANY_SHOPS: "%1$s hat keine Shops." +YOUR_SHOPS: "Deine Shops:" +SHOPS_OF_A: "Shops von %1$s:" +ITEM: "Gegenstand" +AMOUNT: "Betrag" +POSSIBLE_COMMANDS_FOR_THE_PLUGIN: "Gültige Commands für %1$s Plugin:" +TC_HELP_THIS_TEXT: "Dieser Hilfetext" +TC_SHOPS: "Listet die Shops eines Spielers" +TC_CURRENCY_OPT_PARAM_GETSET_CURRENCY: "Währung setzen und anzeigen" +TC_CURRENCY_GET_CURRENCY: "Derzeitige Währung anzeigen" +TC_CAN_PLAYER: "Zeigt die Rechte eines Spielers" +TC_RELOAD: "Lädt das Script mit allen Einstellungen neu" +RESTARTING_PLUGIN: "Restarting %1$s" +RESTARTING_PLUGIN_DONE: "Restarting %1$s komplett" + +# TradeCraftBlockListener.java +ALL_ITEMS_MUST_BE_WITHDRAWN: "Alle Gegenstände und Währung müssen entnommen werden um die Kiste oder Schild zu zerstören!" +YOU_CANT_DESTROY_THIS_SIGN: "Du kannst dieses Schild nicht zerstören!" +YOU_CANT_DESTROY_THIS_CHEST: "Du kannst diese Kiste nicht zerstören!" +YOU_CANT_DESTROY_THIS_BLOCK_ATTACHED: "Du kannst diesen Block nicht zerstören, da ein Schild angebracht ist!" +YOU_CANT_CREATE_INF_SHOPS: "Du kannst keinen unbeschränkten Shop bauen!" +YOU_DONT_HAVE_PERM_CREATE_PLAYER_SHOP: "Du hast keine Berechtigung einen Spieler-Shop zu bauen!" + +# TradeCraftItemShop.java +THE_CHEST_HAS_MORE_THAN_ONE_TYPE: "Diese Kiste hat verschiedene Gegenstände!" +WITHDREW_X_A: "%1$d %2$s entnommen" +WITHDREW_X_CURRENCY_SHOP_STILL_HOLDS_Y_CURRENCY: "Das Maximum von %1$d %2$s entnommen, es sind jetzt noch %3$d %2$s im shop." +WITHDREW_X_A_SHOP_STILL_HOLDS_Y_A: "Das Maximum von %1$d %2$s entnommen, es sind jetzt noch %3$d %2$s im Shop." +THERE_IS_NOTHING_TO_WITHDRAW: "Nichts zum entnehmen vorhanden." +DEPOSITED_X_A: "%1$d %2$s eingezahlt." +YOU_CANT_DEPOSIT_THAT_HERE: "Du kannst dies hier nicht einzahlen!" +YOU_CAN_BUY_Y_A_FOR_X_B: "Du kannst %1$d %2$s für %3$d %4$s kaufen." +YOU_CAN_BUY_X_A_FOR_Y_B_UP_TO_Z: "Du kannst %1$d %2$s für %3$d %4$s kaufen, maximal %5$d." +YOU_CAN_SELL_X_A_FOR_Y_B: "Du kannst %1$d %2$s für %3$d %4$s verkaufen." +YOU_CAN_SELL_X_A_FOR_Y_B_UP_TO_Z: "Du kannst %1$d %2$s für %3$d %4$s verkaufen, maximal %5$d %4$s." +THIS_IS_AN_INFINITE_SHOP: "Dies ist ein unbegrenzter Shop" +YOU_ARE_NOT_ALLOWED_TO_BUY: "Du hast kein Recht in Shops zu kaufen!" +YOU_ARE_NO_ALLOWED_TO_SELL: "Du hast kein Recht in Shops zu verkaufen!" +YOU_CANT_SELL_THAT: "Dies kannst du hier nicht verkaufen!" +YOU_CANT_BUY_HERE: "Du kannst hier nicht kaufen!" +YOU_NEED_TO_SPEND_AT_LEAST_X_A_TO_GET_ANY_B: "Du musst mindestens %1$d %2$s einzahlen, um %3$s zu bekommen." +CANT_BUY_SHOP_HAS_NO_A_LEFT: "Du kannst nichts mehr kaufen, da dieser Shop keine %1$s mehr hat." +CANNOT_BUY_SHOP_ONLY_HAS_X_A: "Du kannst nichts mehr kaufen, da dieser Shop nur noch %1$d %2$s hat." +YOU_NEED_TO_SELL_AT_LEAST_X_A_TO_GET_ANY_B: "Du musst mindestens %1$d %2$s verkaufen, um %3$s zu bekommen." +CANNOT_SELL_SHOP_ONLY_HAS_X_A: "Du kannst nichts mehr verkaufen, da dieser Shop nur noch %1$d %2$s hat." +YOU_SOLD_X_A_FOR_Y_B: "Du hast %1$d %2$s für %3$d %4$s verkauft." +YOU_BOUGHT_X_A_FOR_Y_B: "Du hast %1$d %2$s für %3$d %4$s gekauft." +THIS_SHOP_ALWAYS_RETURNS_TOO_MUCH: "Dieser Shop gibt mehr aus als die Kiste beinhaltet, kontaktiere bitte den Shop-Besitzer." +THIS_SHOP_WOULD_RETURN_TOO_MANY_BUY_LESS: "Dieser Shop versucht mehr auszugeben als die Kiste beinhaltet, versuche weniger zu kaufen." +THIS_SHOP_ALWAYS_RETURNS_TOO_MUCH_CURRENCY: "Dieser Shop gibt mehr %1$s aus als die Kiste beinhaltet, kontaktiere bitte den Shop-Besitzer." +THIS_SHOP_WOULD_RETURN_TOO_MUCH_CURRENCY_SELL_LESS: "Dieser Shop versucht mehr %1$s auszugeben als die Kiste beinhaltet, versuche weniger zu verkaufen." + +# TradeCraftRepairShop.java +IT_COSTS_X_A_TO_REPAIR_AN_ITEM: "Es kostet %1$d %2$s um den Gegenstand zu reparieren." +WITH_THIS_MUCH_A_YOU_CAN_REPAIR_Y_ITEMS: "With this much %1$s you can repair %2$d items." +THAT_IS_NOT_ENOUGH_A: "That's not enough %1$s." +YOU_NEED_X_A_TO_REPAIR_ALL_THIS: "You need %1$d %2$s to repair all this." +YOU_REPAIRED_X_ITEMS_FOR_Y_B: "You repaired %1$d items for %2$d %3$s." +#Translation by Jobsti. Important: UTF-8 without BOM \ No newline at end of file diff --git a/src/TradeCraft.properties b/src/TradeCraft.properties new file mode 100644 index 0000000..1ef048f --- /dev/null +++ b/src/TradeCraft.properties @@ -0,0 +1,27 @@ +#whether to allow creation and use of infinite shops at all +infinite-shops-enabled: true +#whether to allow creation and use of player owned shops at all +player-owned-shops-enabled: true +#the object type ID to set the currency +currency-id: 266 +#the data value so set a sub type for currency +currency-data: 0 +#whether to use the basic game's default stack size for items. Will use limit of 64 when set to false +normal-stack-size: true +#whether to log interaction with shops +log-shop-use: false +#the language to use for language files +language: "en" +#whether to automatically update language files from the jar when there have been changes +auto-update-language-files: true +#repair shops are not tested/supported yet +repair-shops-enabled: false +repair-cost: 10 +#to print debug messages or not +enable-debug-messages: false +#show the world and coordinates when using /tcshops +show-shop-location: false +#the maximum amount of player owned shops a player can have in one world +player-world-shop-limit: 5 +#the maximum amount of player owned shops a player can have on the whole server +player-total-shop-limit: 10 \ No newline at end of file diff --git a/src/TradeCraft.sv.lang b/src/TradeCraft.sv.lang new file mode 100644 index 0000000..717d4f6 --- /dev/null +++ b/src/TradeCraft.sv.lang @@ -0,0 +1,71 @@ +#Swedish translation thanks to cayeen (on dev.bukkit.org) +# TradeCraft.java +ERROR_IN_FORMAT_STRING: "There was an error in the format string:" +NO_PERMISSION_SET_CURRENCY: "Du har inte tillstånd att välja valutan!" +IS_NO_VALID_CURRENCY_USE_INSTEAD: "%1$s är inte ett okej val av föremål, använd 'id[;data]' or 'itemname'." +INVALID_CURRENCY: "Felaktigt valuta val: %1$s" +CURRENCY_IS_SET_TO_A_IDDATA: "Valutan är satt till: %1$s (%2$s)" +CURRENCY_IS_A_IDDATA: "Valutan är: %1$s (%2$s)" +NO_SUCH_PLAYER: "Det finns inte en sådan spelare." +YOU_DONT_OWN_ANY_SHOPS: "Du äger inga affärer!" +A_DOES_NOT_OWN_ANY_SHOPS: "%1$s Äger inga affärer." +YOUR_SHOPS: "Dina affärer:" +SHOPS_OF_A: "Affärer som ägs av %1$s:" +ITEM: "Föremål" +AMOUNT: "Mängd" +POSSIBLE_COMMANDS_FOR_THE_PLUGIN: "Tillgängliga kommandon för %1$s pluginet:" +TC_HELP_THIS_TEXT: "this help text" +TC_SHOPS: "listar alla affärer" +TC_PSHOPS: "listar alla affärer som spelaren äger" +TC_CURRENCY_OPT_PARAM_GETSET_CURRENCY: "få och välj valutan" +TC_CURRENCY_GET_CURRENCY: "få valutan ni använder" +TC_CAN_PLAYER: "visar tillåtelser som spelaren har" +TC_RELOAD: "startar om och med omstartningen laddas alla filer också om" +RESTARTING_PLUGIN: Startar om %1$s" +RESTARTING_PLUGIN_DONE: "Omstartningen av %1$s är färdig" + +# TradeCraftBlockListener.java +ALL_ITEMS_MUST_BE_WITHDRAWN: "Alla föremål och valutan måste ha tagits ut från affären innan du kan förstöra den!" +YOU_CANT_DESTROY_THIS_SIGN: "Du kan inte förstöra den skylten!" +YOU_CANT_DESTROY_THIS_CHEST: "Du kan inte förstöra den kistan!" +YOU_CANT_DESTROY_THIS_BLOCK_ATTACHED: "Du kan inte förstöra det blocket för det sitten en skylt på den!" +YOU_CANT_CREATE_INF_SHOPS: "Du kan inte skapa statliga affärer!" +YOU_DONT_HAVE_PERM_CREATE_PLAYER_SHOP: "Du har inte tillstånd att skapa en affär!" + +# TradeCraftItemShop.java +THE_CHEST_HAS_MORE_THAN_ONE_TYPE: "Den här kistan har mer än en sorts föremål i sig!" +WITHDREW_X_A: "Tog ut %1$d %2$s." +WITHDREW_X_CURRENCY_SHOP_STILL_HOLDS_Y_CURRENCY: "Tog ut det maximala antalet %1$d %2$s, det finns fortfarande %3$d %2$s i affären." +WITHDREW_X_A_SHOP_STILL_HOLDS_Y_A: "Tog ut det maximala antalet %1$d %2$s, det finns fortfarande %3$d %2$s i affären." +THERE_IS_NOTHING_TO_WITHDRAW: "Det finns inget att ta ut." +DEPOSITED_X_A: "Fyllde på med %1$d %2$s." +YOU_CANT_DEPOSIT_THAT_HERE: "Du kan inte fylla på det här!" +YOU_CAN_BUY_Y_A_FOR_X_B: "Du kan köpa %1$d %2$s för %3$d %4$s." +YOU_CAN_BUY_X_A_FOR_Y_B_UP_TO_Z: "Du kan köpa %1$d %2$s för %3$d %4$s, och upp till %5$d." +YOU_CAN_SELL_X_A_FOR_Y_B: "Du kan sälja %1$d %2$s för %3$d %4$s." +YOU_CAN_SELL_X_A_FOR_Y_B_UP_TO_Z: "Du kan sälja %1$d %2$s för %3$d %4$s, och upp till %5$d %4$s." +THIS_IS_AN_INFINITE_SHOP: "Detta är en oändlig butik" +YOU_ARE_NOT_ALLOWED_TO_BUY: "Du är inte tillåten att handla från affärer!" +YOU_ARE_NO_ALLOWED_TO_SELL: "Du är inte tillåten att sälja till affärer!" +YOU_CANT_SELL_THAT: "Du kan inte sälja det här!" +YOU_CANT_BUY_HERE: "Du kan inte köpa här!" +YOU_NEED_TO_SPEND_AT_LEAST_X_A_TO_GET_ANY_B: "Du måste spendera minst %1$d %2$s för att få några %3$s." +CANT_BUY_SHOP_HAS_NO_A_LEFT: "Du kan inte köpa det, affären har inge %1$s kvar." +CANNOT_BUY_SHOP_ONLY_HAS_X_A: "Kan inte köpa så många, affären har bara %1$d %2$s." +YOU_NEED_TO_SELL_AT_LEAST_X_A_TO_GET_ANY_B: "Du måste sälja minst %1$d %2$s för att få några %3$s." +CANNOT_SELL_SHOP_ONLY_HAS_X_A: "Du kan inte sälja affären har bara %1$d %2$s." +YOU_SOLD_X_A_FOR_Y_B: "Du sålde %1$d %2$s för %3$d %4$s." +SOLD_X_A_FOR_Y_B: "sålt %1$d %2$s för %3$d %4$s." +YOU_BOUGHT_X_A_FOR_Y_B: "Du köpte %1$d %2$s för %3$d %4$s." +BOUGHT_X_A_FOR_Y_B: "Köpt %1$d %2$s för %3$d %4$s." +THIS_SHOP_ALWAYS_RETURNS_TOO_MUCH: "Den här affären ger för mycket items varje gång, Kontakta en admin." +THIS_SHOP_WOULD_RETURN_TOO_MANY_BUY_LESS: "Du ger affären för mycket pengar testa handla med mindre." +THIS_SHOP_ALWAYS_RETURNS_TOO_MUCH_CURRENCY: "Den här affären ger alltid mer %1$s än vad den kan innehålla, Kontakta en admin." +THIS_SHOP_WOULD_RETURN_TOO_MUCH_CURRENCY_SELL_LESS: "Den här affären lämnar alltid ifrånsig mer %1$s än vad kistan kan innehålla, testa sälja lite mindre mängd med föremål." + +# TradeCraftRepairShop.java +IT_COSTS_X_A_TO_REPAIR_AN_ITEM: "It costs %1$d %2$s to repair an item." +WITH_THIS_MUCH_A_YOU_CAN_REPAIR_Y_ITEMS: "With this much %1$s you can repair %2$d items." +THAT_IS_NOT_ENOUGH_A: "That's not enough %1$s." +YOU_NEED_X_A_TO_REPAIR_ALL_THIS: "You need %1$d %2$s to repair all this." +YOU_REPAIRED_X_ITEMS_FOR_Y_B: "You repaired %1$d items for %2$d %3$s." diff --git a/TradeCraft.txt b/src/TradeCraft.txt similarity index 91% rename from TradeCraft.txt rename to src/TradeCraft.txt index 2b13d93..b5cb4df 100644 --- a/TradeCraft.txt +++ b/src/TradeCraft.txt @@ -14,3 +14,4 @@ Obsidian, 49, 1:32 Diamond, 264, 1:64 Bread, 297, 4:1 Glass, 20 +LGWool, 35;8, 10:1 diff --git a/src/items.yml b/src/items.yml new file mode 100644 index 0000000..25ddf02 --- /dev/null +++ b/src/items.yml @@ -0,0 +1,98 @@ +Cobblestone: + itemTypeId: 4 + itemTypeData: 0 + buyAmount: 64 + buyValue: 1 + sellAmount: 64 + sellValue: 1 +Gravel: + itemTypeId: 13 + itemTypeData: 0 + buyAmount: 32 + buyValue: 1 + sellAmount: 0 + sellValue: 0 +Sand: + itemTypeId: 12 + itemTypeData: 0 + buyAmount: 32 + buyValue: 1 + sellAmount: 48 + sellValue: 1 +Dirt: + itemTypeId: 3 + itemTypeData: 0 + buyAmount: 32 + buyValue: 1 + sellAmount: 32 + sellValue: 1 +Redstone: + itemTypeId: 331 + itemTypeData: 0 + buyAmount: 16 + buyValue: 1 + sellAmount: 16 + sellValue: 1 +Log: + itemTypeId: 17 + itemTypeData: 0 + buyAmount: 8 + buyValue: 1 + sellAmount: 8 + sellValue: 1 +Coal: + itemTypeId: 263 + itemTypeData: 0 + buyAmount: 4 + buyValue: 1 + sellAmount: 4 + sellValue: 1 +Clay: + itemTypeId: 337 + itemTypeData: 0 + buyAmount: 2 + buyValue: 1 + sellAmount: 2 + sellValue: 1 +Iron: + itemTypeId: 265 + itemTypeData: 0 + buyAmount: 1 + buyValue: 4 + sellAmount: 1 + sellValue: 4 +Obsidian: + itemTypeId: 49 + itemTypeData: 0 + buyAmount: 1 + buyValue: 32 + sellAmount: 1 + sellValue: 32 +Diamond: + itemTypeId: 264 + itemTypeData: 0 + buyAmount: 1 + buyValue: 64 + sellAmount: 1 + sellValue: 64 +Bread: + itemTypeId: 297 + itemTypeData: 0 + buyAmount: 4 + buyValue: 1 + sellAmount: 4 + sellValue: 1 +Glass: + itemTypeId: 20 + itemTypeData: 0 + buyAmount: 0 + buyValue: 0 + sellAmount: 0 + sellValue: 0 +LGWool: + itemTypeId: 35 + itemTypeData: 8 + buyAmount: 10 + buyValue: 1 + sellAmount: 10 + sellValue: 1 diff --git a/src/nl/armeagle/Configuration/StatefulYamlConfiguration.java b/src/nl/armeagle/Configuration/StatefulYamlConfiguration.java new file mode 100644 index 0000000..4e38c35 --- /dev/null +++ b/src/nl/armeagle/Configuration/StatefulYamlConfiguration.java @@ -0,0 +1,79 @@ +package nl.armeagle.Configuration; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.bukkit.Bukkit; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.plugin.java.JavaPlugin; +import org.yaml.snakeyaml.error.YAMLException; + +public class StatefulYamlConfiguration extends YamlConfiguration { + protected File file; + + public StatefulYamlConfiguration(File file) { + this.file = file; + } + + public void load() throws IOException { + this.load(false); + } + public void load(boolean forceDefaults) throws IOException { + boolean notLoaded = false; + if (this.file == null) { + throw new IllegalArgumentException("File cannot be null"); + } + + YamlConfiguration baseConfig = new YamlConfiguration(); + + try { + baseConfig.load(this.file); + } catch (FileNotFoundException fnf) { + notLoaded = true; + } catch (IOException ex) { + Bukkit.getLogger().log(Level.SEVERE, "Cannot load " + this.file, ex); + } catch (InvalidConfigurationException ex) { + if (ex.getCause() instanceof YAMLException) { + Bukkit.getLogger().severe("Config file " + this.file + " isn't valid! " + ex.getCause()); + } else if ((ex.getCause() == null) || (ex.getCause() instanceof ClassCastException)) { + Bukkit.getLogger().severe("Config file " + this.file + " isn't valid!"); + } else { + Bukkit.getLogger().log(Level.SEVERE, "Cannot load " + this.file + ": " + ex.getCause().getClass(), ex); + } + } + + if (notLoaded || forceDefaults) { + InputStream defaultInput = this.getClass().getResourceAsStream("/" + file.getName()); + if ( null != defaultInput ) { + YamlConfiguration defaultConfig = YamlConfiguration.loadConfiguration(defaultInput); + + try { + this.loadFromString(defaultConfig.saveToString()); + } catch (InvalidConfigurationException e) { + Logger.getLogger(JavaPlugin.class.getName()).log(Level.SEVERE, "Invalid default configuation"); + } + this.save(); + } + } else if (! notLoaded) { + try { + this.loadFromString(baseConfig.saveToString()); + } catch (InvalidConfigurationException e) { + Logger.getLogger(JavaPlugin.class.getName()).log(Level.SEVERE, "Invalid configuation"); + } + } + + } + + public void save() throws IOException { + super.save(this.file); + } + + public File getFile() { + return this.file; + } +} diff --git a/src/nl/armeagle/TradeCraft/TradeCraft.java b/src/nl/armeagle/TradeCraft/TradeCraft.java new file mode 100644 index 0000000..ccc40ae --- /dev/null +++ b/src/nl/armeagle/TradeCraft/TradeCraft.java @@ -0,0 +1,652 @@ +package nl.armeagle.TradeCraft; + +import java.io.*; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.IllegalFormatException; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import nl.armeagle.Configuration.StatefulYamlConfiguration; + +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.Server; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.Chest; +import org.bukkit.block.Sign; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.material.MaterialData; +import org.bukkit.plugin.PluginDescriptionFile; +import org.bukkit.plugin.PluginManager; +import org.bukkit.plugin.java.JavaPlugin; + +public class TradeCraft extends JavaPlugin { + public static enum MessageTypes {WITHDRAW, DEPOSIT}; + + // The plugin name. + static final String pluginName = "TradeCraft"; + + public static final Pattern itemPatternIdSplitData = Pattern.compile("^(\\d+)(?:;(\\d+))?$"); + + private static enum Commands { + tcsetcurrency, + tcgetcurrency, + tcshops, + tcpshops, + tcreload, + tcplayerperms, + tchelp, + tc + }; + + // Stuff used to interact with the server. + final Logger log = Logger.getLogger("Minecraft"); + final Server server = this.getServer(); + + protected BufferedWriter usageLog = null; + // Objects used by the plugin. + static TradeCraftItem currency; + static TradeCraftPropertiesFile properties; + TradeCraftConfigurationFile configuration; + public TradeCraftLocalization localization; + TradeCraftDataFile data; + + private HashMap configs = new HashMap(); + + private final TradeCraftBlockListener blockListener = new TradeCraftBlockListener(this); + private final TradeCraftPlayerListener playerListener = new TradeCraftPlayerListener(this); + public TradeCraftPermissions permissions = new TradeCraftPermissions(this); + + // prevent the script from registering the event listeners multiple times (by dis-/enable) + public static boolean hasRegisteredEventListeners = false; + + public void onDisable() { + this.disable(); + } + private void disable() { + if ( this.usageLog != null ) { + try { + this.usageLog.close(); + } catch (IOException e) { + this.log(Level.WARNING, "Failed to close shop usage log file"); + } + } + properties = null; + configuration = null; + this.localization = null; + data.save(); + data = null; + } + + public void onEnable() { + this.enable(); + } + private void enable() { + properties = new TradeCraftPropertiesFile(this); + configuration = new TradeCraftConfigurationFile(this); + data = new TradeCraftDataFile(this); + this.localization = new TradeCraftLocalization(this); + + if ( TradeCraft.properties.logShopUse() ) { + File usageLogFile = new File(this.getDataFolder(), "shopUsage.log"); + try { + if ( !usageLogFile.exists() ) { + usageLogFile.createNewFile(); + } + if ( usageLogFile.canWrite() ) { + this.usageLog = new BufferedWriter(new FileWriter(usageLogFile, true)); + this.log(Level.INFO, "Writing shop usage to log file: "+ usageLogFile.toString()); + } else { + this.log(Level.WARNING, "Error opening shop usage log file: "+ usageLogFile.toString()); + } + } catch (IOException e) { + this.log(Level.WARNING, "Failed to open shop usage log file: "+ usageLogFile.toString()); + } + } + + configuration.load(); + data.load(); + currency = properties.getCurrencyType(); + + if ( !TradeCraft.hasRegisteredEventListeners ) { + PluginManager pm = this.getServer().getPluginManager(); + pm.registerEvents(playerListener, this); + pm.registerEvents(blockListener, this); + TradeCraft.hasRegisteredEventListeners = true; + } + + PluginDescriptionFile pdfFile = this.getDescription(); + this.log.info(pdfFile.getName() + " version " + pdfFile.getVersion() + " is enabled!"); + + } + + @Override + public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { + try { + TradeCraft.Commands command = TradeCraft.Commands.valueOf(cmd.getName()); + if (sender instanceof Player) { + Player p = (Player) sender; + switch (command) { + case tcsetcurrency: + if ( args.length == 1 && this.permissions.canSetCurrency(p) ) { + TradeCraftItem testCurrency = null; + // try to split ID and Data, separated by a semicolon mark + Matcher IdSplitData = TradeCraft.itemPatternIdSplitData.matcher(args[0]); + + if ( !IdSplitData.matches() ) { + // try to match the parameter to item names from the configuration + TradeCraftConfigurationInfo setCurr = this.configuration.get(args[0]); + if ( setCurr == null ) { + this.sendMessage(p, TradeCraftLocalization.get("IS_NO_VALID_CURRENCY_USE_INSTEAD"), + args[0]); + return false; + } else { + currency = setCurr.type; + } + } else { + try { + int cid = Integer.parseInt(IdSplitData.group(1)); + if ( IdSplitData.group(2) != null ) { + testCurrency = new TradeCraftItem(cid, Short.parseShort(IdSplitData.group(2))); + } else { + testCurrency = new TradeCraftItem(cid); + } + } catch ( NumberFormatException e ) { + this.sendMessage(p, TradeCraftLocalization.get("INVALID_CURRENCY"), + args[0]); + return false; + } + if ( this.configuration.get(testCurrency) != null ) { + currency = testCurrency; + } else { + this.sendMessage(p, TradeCraftLocalization.get("INVALID_CURRENCY"), + args[0]); + return false; + } + } + + TradeCraft.properties.setCurrencyType(currency); + this.sendMessage(p, TradeCraftLocalization.get("CURRENCY_IS_SET_TO_A_IDDATA"), + this.getCurrencyName(), + currency.toShortString()); + return true; + } + return true; + case tcgetcurrency: + this.sendMessage(p, TradeCraftLocalization.get("CURRENCY_IS_A_IDDATA"), + this.getCurrencyName(), + currency.toShortString()); + return true; + case tcshops: + // lookup own shows + displayShops(p.getName(), p, false); + return true; + case tcpshops: + // Check whether another parameter is passed, if so check whether + // the player can get information about other player's shops. + if ( this.permissions.canQueryOtherShops(p) ) { + if ( args.length == 1 ) { + // lookup other player's shops + displayShops(args[0], p, true); + } else { + this.sendMessage(p, cmd.getUsage()); + } + } + return true; + case tcreload: + if ( this.permissions.canReload(p)) { + this.sendMessage(p, TradeCraftLocalization.get("RESTARTING_PLUGIN"), TradeCraft.pluginName); + this.disable(); + this.enable(); + this.sendMessage(p, TradeCraftLocalization.get("RESTARTING_PLUGIN_DONE"), TradeCraft.pluginName); + } + return true; + case tcplayerperms: + if ( this.permissions.canQueryPlayer(p)) { + if ( args.length == 1 ) { + permissions.debug(sender, args[0]); + } else { + sender.sendMessage(cmd.getUsage()); + } + } + return true; + default: + displayCommandHelpText(p); + return true; + } + } else if ( sender instanceof ConsoleCommandSender ) { + switch (command) { + case tcplayerperms: + if ( args.length == 1 ) { + permissions.debug(sender, args[0]); + } else { + sender.sendMessage(cmd.getUsage()); + } + return true; + case tcpshops: + // Check whether another parameter is passed, if so check whether + // the player can get information about other player's shops. + if ( args.length == 1 ) { + // lookup other player's shops + displayShops(args[0], sender, true); + } else { + sender.sendMessage(cmd.getUsage()); + } + return true; + case tcreload: + sender.sendMessage(String.format(TradeCraftLocalization.get("RESTARTING_PLUGIN"), + TradeCraft.pluginName)); + this.disable(); + this.enable(); + sender.sendMessage(String.format(TradeCraftLocalization.get("RESTARTING_PLUGIN_DONE"), + TradeCraft.pluginName)); + return true; + default: + displayCommandHelpText(null); + return true; + } + } + } catch (IllegalArgumentException e) { + return false; + } + return false; + } + + void displayShops(String infoPlayerName, CommandSender displayTo, boolean otherQuery) { + Map list = data.shopsOwned(infoPlayerName); + if (list.size() == 0) { + if ( otherQuery ) { + // elevated player looking for other player's shops + displayTo.sendMessage(String.format(TradeCraftLocalization.get("A_DOES_NOT_OWN_ANY_SHOPS"), + infoPlayerName)); + } else { + displayTo.sendMessage(TradeCraftLocalization.get("YOU_DONT_OWN_ANY_SHOPS")); + } + return; + } + + if ( otherQuery ) { + displayTo.sendMessage(String.format(TradeCraftLocalization.get("SHOPS_OF_A"), + infoPlayerName)); + } else { + displayTo.sendMessage(TradeCraftLocalization.get("YOUR_SHOPS")); + } + for (Map.Entry entry : list.entrySet()) { + TradeCraftDataInfo info = entry.getValue(); + String message = ""; + if (TradeCraft.properties.showShopLocation()) { + String location = entry.getKey().replaceFirst(",", "(") +")"; + message += ChatColor.GRAY + location +" "+ ChatColor.WHITE; + } + message += TradeCraftLocalization.get("ITEM") +": "+ this.configuration.get(info.itemType).name +"("+ info.itemType.toShortString() +")" + +" "+ TradeCraftLocalization.get("AMOUNT") +": "+ info.itemAmount +" " + + this.getCurrencyName() +": "+ info.currencyAmount; + + displayTo.sendMessage(message); + } + + } + + void sendMessage(Player player, TradeCraft.MessageTypes messageType, String format, Object... args) { + try { + String message = String.format(format, args); + player.sendMessage(TradeCraft.properties.getMessageTypeColor(messageType) + message); + } catch ( IllegalFormatException e ) { + player.sendMessage(TradeCraftLocalization.get("ERROR_IN_FORMAT_STRING") +" "+ format); + } + } + + void sendMessage(Player player, String format, Object... args) { + try { + String message = String.format(format, args); + player.sendMessage(message); + } catch ( IllegalFormatException e ) { + player.sendMessage(TradeCraftLocalization.get("ERROR_IN_FORMAT_STRING") +" "+ format); + } + } + + void trace(Player player, String format, Object... args) { + if (properties.getEnableDebugMessages()) { + if (null != player) { + sendMessage(player, format, args); + } else { + this.log(Level.INFO, format, args); + } + } + } + /* + * When a block behind a shop sign is destroyed, the sign would be destroyed too. + * Check all side faces of this block, for a sign attached to this block. Then + * pass that sign block to getShopFromSignBlock. + * + * This should only be used for checking whether a normal block can be destroyed, for there not being any + * signs attached to it, or this block being a chest or sign itself. + * Since one block can + */ + ArrayList getShopsFromBlock(Player player, Block block) { + ArrayList shops = new ArrayList(); + // use other function(s) directly if applicable + if ( block.getType() == Material.CHEST || block.getType() == Material.WALL_SIGN ) { + TradeCraftShop shop = getShopFromSignOrChestBlock(player, block); + if ( shop != null ) { + shops.add(shop); + } + } else { + // go through all 4 faces of this block to check for a sign attached to this block + final BlockFace[] sides = {BlockFace.NORTH, BlockFace.EAST, BlockFace.SOUTH, BlockFace.WEST}; + for ( int index_sides = 0; index_sides < sides.length; index_sides++ ) { + BlockFace side = sides[index_sides]; + // get the block on that side + Block sideBlock = block.getRelative(side); + // check for it being a wall sign + if ( sideBlock.getType() == Material.WALL_SIGN ) { + // get the sign (extending MaterialData) for clean attached face checking + org.bukkit.material.Sign materialSign = new org.bukkit.material.Sign(Material.WALL_SIGN, sideBlock.getData()); + /* Now, for easy comparison, we'll compare to the direction the sign is facing. If that's + * the same as the current face (of the 4 we're looping through), then this sign is attached + * to the block that is being destroyed. + */ + if ( materialSign.getFacing() == side ) { + TradeCraftShop shop = this.getShopFromSignBlock(player, sideBlock); + if ( shop != null ) { + shops.add(shop); + } + } + + } + } + } + return shops; + } + TradeCraftShop getShopFromSignOrChestBlock(Player player, Block block) { + if (block.getType() == Material.CHEST) { + block = block.getWorld().getBlockAt( + block.getX(), + block.getY() + 1, + block.getZ() + ); + } + + return getShopFromSignBlock(player, block); + } + + TradeCraftShop getShopFromSignBlock(Player player, Block block) { + if (block.getType() != Material.WALL_SIGN) { + return null; + } + + int x = block.getX(); + int y = block.getY(); + int z = block.getZ(); + + trace(player, "You clicked a sign at %d, %d, %d in world: %s.", x, y, z, block.getWorld().getName()); + + Sign sign = (Sign) block.getWorld().getBlockAt(x, y, z).getState(); + + // The sign at this location can be null if it was just destroyed. + if (sign == null) { + trace(player, "The sign is no longer there."); + + return null; + } + + String itemName = getItemName(sign.getLines()); + + if (itemName == null) { + trace(player, "There is no item name on the sign."); + + return null; + } + + trace(player, "The item name on the sign is %s.", itemName); + + Block blockBelowSign = block.getRelative(0, -1, 0); + + if (blockBelowSign.getType() != Material.CHEST) { + trace(player, "There is no chest beneath the sign."); + return null; + } + + Chest chest = (Chest) blockBelowSign.getState(); + + if (itemName.toLowerCase().equals("repair")) { + if (!properties.getRepairShopsEnabled()) { + trace(player, "Repair shops are not enabled."); + return null; + } + + if (player == null || !player.isOp()) { + trace(player, "You can't use repair shops."); + return null; + } + + trace(player, "This is a repair shop."); + return new TradeCraftRepairShop(this, sign, chest); + } + + if (!configuration.isConfigured(itemName)) { + trace(player, + "The item name %s is not configured in the config file.", + itemName); + return null; + } + + // TODO change to use chest getOwner + //String ownerName = getOwnerName(sign.getLine(3)); + String ownerName = data.getOwnerOfSign(sign); + + if (ownerName == null) { + trace(player, "There is no owner name on the sign."); + + if (!properties.getInfiniteShopsEnabled()) { + trace(player, "Ininite shops are not enabled."); + return null; + } + + trace(player, "This is an infinite shop."); + try { + return new TradeCraftInfiniteShop(this, sign, chest); + } catch (Exception e) { + trace(player, e.getMessage()); + } + return null; + } + + trace(player, "The owner name on the sign is %s.", ownerName); + + if (!properties.getPlayerOwnedShopsEnabled()) { + trace(player, "Player-owned shops are not enabled."); + return null; + } + + trace(player, "This is a player-owned shop."); + return new TradeCraftPlayerOwnedShop(this, sign, chest, ownerName); + } + + String getItemName(String[] signLines) { + return getSpecialText(signLines, "[", "]"); + } + + String getOwnerName(String signLine) { + return getSpecialTextOnLine(signLine, "-", "-"); + } + + private String getSpecialText(String[] signLines, String prefix, String suffix) { + for (int i = 0; i < 4; i++) { + String text = getSpecialTextOnLine(signLines[i], prefix, suffix); + + if (text != null) { + return text; + } + } + + return null; + } + + private String getSpecialTextOnLine(String signLine, String prefix, String suffix) { + if (signLine == null) { + return null; + } + + signLine = signLine.trim(); + + if (signLine.startsWith(prefix) && signLine.endsWith(suffix) + && signLine.length() > 2) { + + String text = signLine.substring(1, signLine.length() - 1); + text = text.trim(); + if (text.equals("")) { + return null; + } + + return text; + } + + return null; + } + + TradeCraftExchangeRate getExchangeRate(String[] signLines, int lineNumber) { + if ( lineNumber < 0 || lineNumber >= signLines.length ) { + return null; + } + return getExchangeRate(signLines[lineNumber]); + } + TradeCraftExchangeRate getExchangeRate(String signLine) { + return new TradeCraftExchangeRate(signLine); } + + static int getMaxStackSize(int itemType) { + if ( TradeCraft.properties.getNormalStackSizeUsed() ) { + int stackSize = Material.getMaterial(itemType).getMaxStackSize(); + return (stackSize == 0 ? 64 : Material.getMaterial(itemType).getMaxStackSize()); + } else { + return 64; + } + } + + /** + * Get a CamelCased string based on the current currency. + * + * @return a string representing the currency. + */ + public String getCurrencyName() { + // Try to get the name from the configuration file first + TradeCraftConfigurationInfo configInfo = this.configuration.get(TradeCraft.currency); + if ( configInfo != null ) { + return configInfo.name; + } else { + + ItemStack currencyStack = new ItemStack(TradeCraft.currency.id, 1, TradeCraft.currency.data); // weird that there's no Material.getMaterial(id, short/byte) + MaterialData currencyData = currencyStack.getType().getNewData((byte)TradeCraft.currency.data); + String currencyString; + if ( currencyData == null ) { + currencyString = currencyStack.getType().name(); + } else { + currencyString = currencyData.toString(); + } + + //String baseName = stack.getType().name(); + String[] words = currencyString.replace("null ", "").split("\\(")[0].split("[ _]{1}"); + String name = ""; + for ( int word_ind = 0; word_ind < words.length; word_ind++ ) { + String word = words[word_ind]; + if ( word_ind > 0 ) { + name = name.concat(" "); + } + name = name.concat(word.substring(0, 1).toUpperCase()).concat(word.substring(1).toLowerCase()); + } + return name; + } + } + + private void displayCommandHelpText(Player player) { + if ( player != null ) { + this.sendMessage(player, TradeCraftLocalization.get("POSSIBLE_COMMANDS_FOR_THE_PLUGIN"), + TradeCraft.pluginName); + this.sendMessage(player, "/tc[help]"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_HELP_THIS_TEXT")); + this.sendMessage(player, "/tcshops"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_SHOPS")); + if ( this.permissions.canQueryOtherShops(player) ) { + this.sendMessage(player, "/tcpshops [player]"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_PSHOPS")); + } + this.sendMessage(player, "/tcgetcurrency"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_CURRENCY_GET_CURRENCY")); + if ( this.permissions.canSetCurrency(player) ) { + this.sendMessage(player, "/tcsetcurrency [id[;data]]"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_CURRENCY_OPT_PARAM_GETSET_CURRENCY")); + } + if ( this.permissions.canReload(player) ) { + this.sendMessage(player, "/tcreload"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_RELOAD")); + } + if ( this.permissions.canQueryPlayer(player) ) { + this.sendMessage(player, "/tcplayerperms"+ ChatColor.GRAY +" "+ TradeCraftLocalization.get("TC_CAN_PLAYER")); + } + } else { + // console command help + this.log(Level.INFO, TradeCraftLocalization.get("POSSIBLE_COMMANDS_FOR_THE_PLUGIN"), + TradeCraft.pluginName); + this.log(Level.INFO, "tc[help]: "+ TradeCraftLocalization.get("TC_HELP_THIS_TEXT")); + this.log(Level.INFO, "tcplayerperms playername: "+ TradeCraftLocalization.get("TC_CAN_PLAYER")); + this.log(Level.INFO, "tcpshops [player]: "+ TradeCraftLocalization.get("TC_PSHOPS")); + this.log(Level.INFO, "tcreload: "+ TradeCraftLocalization.get("TC_RELOAD")); + } + } + + public void saveConfig() { + Logger.getLogger(JavaPlugin.class.getName()).log(Level.INFO, "saving config to: "+ this.getConfig("config").getFile().getAbsolutePath()); + try { + this.getConfig("config").save(); + } catch (IOException ex) { + Logger.getLogger(JavaPlugin.class.getName()).log(Level.SEVERE, "Could not save config to " + this.getConfig("config").getFile().getName(), ex); + } + } + public StatefulYamlConfiguration getConfig() { + return this.getConfig("config"); + } + public StatefulYamlConfiguration getConfig(String name) { + if (name.indexOf(".") < 0) { + name += ".yml"; + } + if (this.configs.containsKey(name)) { + return this.configs.get(name); + } else { + File configFile = new File(this.getDataFolder(), name); + StatefulYamlConfiguration config = new StatefulYamlConfiguration(configFile); + this.configs.put(name, config); + return config; + } + } + + public void log(Level level, String format, Object... args) { + this.log.log(level, TradeCraft.pluginName +": "+ String.format(format, args)); + } + + public void useLog(Player player, TradeCraftShop shop, String format, Object... args) { + if ( TradeCraft.properties.logShopUse() ) { + if ( this.usageLog != null ) { + try { + SimpleDateFormat formatter = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); + this.usageLog.write(String.format("%s %s \t%s %s\n", + formatter.format(new Date()), + shop.toString(), + player.getDisplayName(), + String.format(format, args))); + this.usageLog.flush(); + } catch (IOException e) { + this.log(Level.WARNING, "Failed to write to shop use log file"); + } + } else { + this.log(Level.INFO, "not written to log file"); + } + } + } + +} \ No newline at end of file diff --git a/src/nl/armeagle/TradeCraft/TradeCraftBlockListener.java b/src/nl/armeagle/TradeCraft/TradeCraftBlockListener.java new file mode 100644 index 0000000..10d8e3b --- /dev/null +++ b/src/nl/armeagle/TradeCraft/TradeCraftBlockListener.java @@ -0,0 +1,224 @@ +package nl.armeagle.TradeCraft; + +import java.util.ArrayList; +import java.util.List; + +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.block.Sign; +import org.bukkit.entity.Player; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockPistonExtendEvent; +import org.bukkit.event.block.BlockPistonRetractEvent; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.SignChangeEvent; + +public class TradeCraftBlockListener implements Listener{ + + private TradeCraft plugin; + + TradeCraftBlockListener(TradeCraft plugin){ + this.plugin = plugin; + } + + public void debug(String str){ + plugin.getServer().broadcastMessage(str); + } + + @EventHandler + public void onNormalBlockBreak(BlockBreakEvent e) { + this.onBlockBreak(e, EventPriority.NORMAL); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void onMonitorBlockBreak(BlockBreakEvent e) { + this.onBlockBreak(e, EventPriority.MONITOR); + } + + private void onBlockBreak(BlockBreakEvent e, EventPriority p) { + if ( !this.plugin.isEnabled() ) { + return; + } + + Player player = e.getPlayer(); + Block block = e.getBlock(); + ArrayList shops = plugin.getShopsFromBlock(player, block); + + if (shops.size() == 0) { + return; + } + + // Go through all shops in the list and check whether the player can destroy them all first. + // Only if that is the case proceed to destroy. + if (EventPriority.NORMAL == p) { + for ( TradeCraftShop shop : shops ) { + if (!shop.playerCanDestroy(player) && !plugin.permissions.canDestroyShops(player) || + shop.shopCanBeWithdrawnFrom() ) { + // cannot destroy this shop, so cancel destruction, use distinct error messages + if ( shop.shopCanBeWithdrawnFrom() ) { + plugin.sendMessage(player, TradeCraftLocalization.get("ALL_ITEMS_MUST_BE_WITHDRAWN")); + } else { + if ( block.getType() == Material.WALL_SIGN ) { + plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_DESTROY_THIS_SIGN")); + } else if ( block.getType() == Material.CHEST ) { + plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_DESTROY_THIS_CHEST")); + } else { + plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_DESTROY_THIS_BLOCK_ATTACHED")); + } + } + stopDestruction(block,e); + return; + } + } + } + + if (EventPriority.MONITOR == p && ! e.isCancelled()) { + // player can destroy all shops, so proceed + for ( TradeCraftShop shop : shops ) { + plugin.data.deleteShop(shop); + } + } + } + + @EventHandler(priority = EventPriority.MONITOR) + public void onMonitorSignChange(SignChangeEvent e) { + this.onSignChange(e, EventPriority.MONITOR); + } + + @EventHandler + public void onNormalSignChange(SignChangeEvent e) { + this.onSignChange(e, EventPriority.NORMAL); + } + + public void onSignChange(SignChangeEvent event, EventPriority priority) { + if ( !this.plugin.isEnabled() ) { + return; + } + + // Check whether this block is (still) a sign. Can be revoked already in case the sign was temporary, + // for example when the plugin SimpleSignEdit is being used. + if ( event.getBlock().getType() != Material.SIGN_POST && + event.getBlock().getType() != Material.WALL_SIGN ) { + return; + } + + Sign sign = (Sign) event.getBlock().getState(); + + Player player = event.getPlayer(); + String ownerName = player.getName(); + + String itemName = plugin.getItemName(event.getLines()); + + if (itemName == null) { + plugin.trace(player, "sign change, no item name, ignore"); + return; + } + // Check whether this is an existing item. Try to prevent as little normal sign placement as possible. + // Only treat signs with "[name]" as item line where "name" is found in the configuration (.txt) file. + TradeCraftConfigurationInfo itemInfo = plugin.configuration.get(itemName); + if ( itemInfo == null ) { + plugin.trace(player, "sign change, %s is not a valid item name, ignore this sign", itemName); + return; + } + + TradeCraftExchangeRate buyRate = new TradeCraftExchangeRate(event.getLine(1)); + TradeCraftExchangeRate sellRate = new TradeCraftExchangeRate(event.getLine(2)); + // no buy rate means this is an infinite shop + if ( !buyRate.isValid() && !sellRate.isValid() ) { + if (plugin.permissions.canMakeInfShops(player)){ + plugin.trace(player, "sign change, infinite chest of %s", itemName); + return; + } + + if (EventPriority.NORMAL == priority) { + plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_CREATE_INF_SHOPS")); + event.setCancelled(true); + return; + } + } + // there is a buy rate, so this is a player owned shop + + if (EventPriority.NORMAL == priority && !plugin.permissions.canMakePlayerShops(player)){ + plugin.sendMessage(player, TradeCraftLocalization.get("YOU_DONT_HAVE_PERM_CREATE_PLAYER_SHOP")); + event.setCancelled(true); + return; + } + + // check whether the player doesn't have too many shops + if (EventPriority.NORMAL == priority) { + int totalShopLimit = TradeCraft.properties.getPlayerTotalShopLimit(); + int worldShopLimit = TradeCraft.properties.getPlayerWorldShopLimit(); + if (plugin.data.getPlayerShopCount(player) >= totalShopLimit) { + plugin.sendMessage(player, TradeCraftLocalization.get("TOTAL_SHOP_LIMIT_X"), totalShopLimit); + event.setCancelled(true); + return; + } else if (plugin.data.getPlayerShopCount(player, player.getWorld()) >= worldShopLimit) { + plugin.sendMessage(player, TradeCraftLocalization.get("WORLD_SHOP_LIMIT_X"), worldShopLimit); + event.setCancelled(true); + return; + } + + } + + if (EventPriority.MONITOR == priority && !event.isCancelled()) { + plugin.trace(player, "Setting owner of sign to: %s", ownerName); + // set the player name on the last line + event.setLine(3, "-"+ ownerName.substring(0, Math.min(ownerName.length(), 15)) +"-"); + plugin.data.createNewSign(ownerName, itemInfo, sign); + } + + /* + if ( this.plugin.properties.getStrictPlayerShopOwnerNameRequired() ) { + if (player.getName().equalsIgnoreCase(ownerName)) { + plugin.data.setOwnerOfSign(player.getName(), sign); + return; + } + } else { + if (player.getName().startsWith(ownerName)) { + plugin.data.setOwnerOfSign(player.getName(), sign); + return; + } + } + */ + } + + // prevent pistons from pushing away (the block behind) a sign. Block all retract and extend events that would move a block behind a sign, also for shop owners + @EventHandler + public void onNormalBlockPistonRetract(BlockPistonRetractEvent e) { + if (e.isSticky()) { + Block block = e.getRetractLocation().getBlock(); + ArrayList shops = plugin.getShopsFromBlock(null, block); + + if (shops.size() != 0) { + e.setCancelled(true); + } + } + } + @EventHandler + public void onNormalBlockPistonExtend(BlockPistonExtendEvent e) { + List blocks = e.getBlocks(); + for (Block block:blocks) { + ArrayList shops = plugin.getShopsFromBlock(null, block); + + if (shops.size() != 0) { + e.setCancelled(true); + return; + } + } + } + + public void stopDestruction(Block b, BlockBreakEvent e){ + if(b.getState() instanceof Sign){ + Sign sign = (Sign)b.getState(); + String[] lines = sign.getLines(); + for(int i = 0;i<4;i++){ + sign.setLine(i, lines[i]); + } + + sign.update(true); + } + e.setCancelled(true); + } +} diff --git a/com/mjmr89/TradeCraft/TradeCraftChest.java b/src/nl/armeagle/TradeCraft/TradeCraftChest.java similarity index 51% rename from com/mjmr89/TradeCraft/TradeCraftChest.java rename to src/nl/armeagle/TradeCraft/TradeCraftChest.java index 365c4c2..0f8655f 100644 --- a/com/mjmr89/TradeCraft/TradeCraftChest.java +++ b/src/nl/armeagle/TradeCraft/TradeCraftChest.java @@ -1,38 +1,32 @@ -package com.mjmr89.TradeCraft; +package nl.armeagle.TradeCraft; import java.util.ArrayList; import java.util.List; - -import org.bukkit.Material; import org.bukkit.block.Chest; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; class TradeCraftChest { private Inventory chest; - public int id = 0; + public TradeCraftItem type = new TradeCraftItem(0); public int total = 0; public boolean diffFlag = false; - public TradeCraftChest(Chest c) { chest = c.getInventory(); - for (ItemStack item : chest.getContents()) { - if(!item.getType().equals(Material.AIR)){ - if(id != 0 && id != item.getTypeId()){ - diffFlag = true; - return; - } - id = item.getTypeId(); - total += item.getAmount(); - } - -// if (item != null) { -// addItem(item); -// } + if(item != null){ + short itemData = (item.getData() == null ? (short)0 : item.getDurability()); + if(type.id != 0 && (type.id != item.getTypeId() || type.data != itemData) ){ + diffFlag = true; + return; + } + type.id = item.getTypeId(); + type.data = itemData; + total += item.getAmount(); + } } } @@ -44,27 +38,27 @@ public void clear() { chest.clear(); } - public void add(int id, int amount) { - int maxStackSize = TradeCraft.getMaxStackSize(id); + public void add(TradeCraftItem type, int amount) { + int maxStackSize = TradeCraft.getMaxStackSize(type.id); int blocks = amount / maxStackSize; for (int i = 0; i < blocks; i++) { - chest.addItem(new ItemStack(id, maxStackSize)); + chest.addItem(new ItemStack(type.id, maxStackSize, type.data)); } int remainder = amount % maxStackSize; if (remainder > 0) { - chest.addItem(new ItemStack(id, remainder)); + chest.addItem(new ItemStack(type.id, remainder, type.data)); } } public void update() { } - public void populateChest(int id, int amount) { + public void populateChest(TradeCraftItem type, int amount) { clear(); - add(id, amount); + add(type, amount); update(); } @@ -72,7 +66,8 @@ public int getAmountOfCurrencyInChest() { int amount = 0; for (ItemStack item : ((Inventory)chest).getContents()) { if (item != null) { - if (item.getType() == TradeCraft.currency) { + short itemData = (item.getData() == null ? (short)0 : item.getDurability()); + if (item.getTypeId() == TradeCraft.currency.id && itemData == TradeCraft.currency.data) { amount += item.getAmount(); } } @@ -84,11 +79,16 @@ public List getNonCurrencyItems() { List items = new ArrayList(); for (ItemStack item : chest.getContents()) { if (item != null) { - if (item.getType() != TradeCraft.currency) { + short itemData = (item.getData() == null ? (short)0 : item.getDurability()); + if (item.getTypeId() != TradeCraft.currency.id || itemData != TradeCraft.currency.data) { items.add(item); } } } return items; } + + public int getSize() { + return (this.chest == null ? 0 : this.chest.getSize()); + } } diff --git a/src/nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java b/src/nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java new file mode 100644 index 0000000..589ab2b --- /dev/null +++ b/src/nl/armeagle/TradeCraft/TradeCraftConfigurationFile.java @@ -0,0 +1,199 @@ +package nl.armeagle.TradeCraft; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.bukkit.configuration.MemorySection; + +import nl.armeagle.Configuration.StatefulYamlConfiguration; + +/** + * The name of this class is a bit misleading. This class stores all the items and their default + * trade rates that can be used in the game. The actual configuration of the plugin itself + * is handled by the TradeCraftProperties class. + */ +class TradeCraftConfigurationFile { + private static final String fileName = TradeCraft.pluginName + ".txt"; + private static final String configName = "items"; + + private static final Pattern commentPattern = Pattern.compile("^\\s*#.*$"); + private static final Pattern infoPattern = Pattern.compile( + "^\\s*([^,]+)\\s*," + // name + "\\s*(\\d+(?:;\\d+)?)\\s*" + // id[;data] (optional ;data) + "(?:,\\s*(\\d+)\\s*:\\s*(\\d+))?\\s*" + // buyAmount:buyValue + "(?:,\\s*(\\d+)\\s*:\\s*(\\d+))?\\s*$"); // sellAmount:sellValue + + private HashMap mapItemNames = new HashMap(); + + private final TradeCraft plugin; + // create an index from an TradeCraftItem (id;data) to the TradeCraftConfigurationInfo entry. This for configured name lookup based on id;data + private final Map TCitemInfoIndex = new HashMap(); + + TradeCraftConfigurationFile(TradeCraft plugin) { + this.plugin = plugin; + } + + public StatefulYamlConfiguration getConfig() { + return this.plugin.getConfig(TradeCraftConfigurationFile.configName); + } + + void load() { + StatefulYamlConfiguration config = (StatefulYamlConfiguration) this.getConfig(); + + // if file exists, load the config to it once and then rename the old config + File file = new File(plugin.getDataFolder().getAbsolutePath(), fileName); + if ( file.exists() ) { + try { + FileReader reader = new FileReader(file); + BufferedReader configurationFile = new BufferedReader(reader); + + int lineNumber = 0; + String line; + + while ((line = configurationFile.readLine()) != null) { + lineNumber += 1; + + if (line.trim().equals("")) { + continue; + } + + Matcher commentMatcher = commentPattern.matcher(line); + + if (commentMatcher.matches()) { + continue; + } + + Matcher infoMatcher = infoPattern.matcher(line); + + if (!infoMatcher.matches()) { + plugin.log.warning( + "Failed to parse line number " + lineNumber + + " in " + file.getAbsolutePath() + + ": " + line); + continue; + } + + TradeCraftConfigurationInfo info = new TradeCraftConfigurationInfo(); + info.name = infoMatcher.group(1); + + // try to split ID and Data, separated by a semicolon mark + Matcher IdSplitData = TradeCraft.itemPatternIdSplitData.matcher(infoMatcher.group(2)); + + if (!IdSplitData.matches()) { + plugin.log.info( + "Failed to parse line number " + lineNumber + + " in " + file.getAbsolutePath() + + ": " + line); + continue; + } + + int id = Integer.parseInt(IdSplitData.group(1)); + if ( IdSplitData.group(2) != null ) { + short data = Short.parseShort(IdSplitData.group(2)); + info.type = new TradeCraftItem(id, data); + } else { + info.type = new TradeCraftItem(id); + } + + if (infoMatcher.group(3) != null) { + info.sellAmount = info.buyAmount = Integer.parseInt(infoMatcher.group(3)); + info.sellValue = info.buyValue = Integer.parseInt(infoMatcher.group(4)); + } + + if (infoMatcher.group(5) != null) { + info.sellAmount = Integer.parseInt(infoMatcher.group(5)); + info.sellValue = Integer.parseInt(infoMatcher.group(6)); + } + +// config.set(info.name.toUpperCase(), info.toMemoryConfiguration()); + Iterator> iter = info.toMap().entrySet().iterator(); + while (iter.hasNext()) { + Map.Entry pairs = (Map.Entry)iter.next(); + if (!pairs.getKey().equals("name")) { + config.set(info.name +"."+ pairs.getKey(), pairs.getValue()); + } + } + +// TCitemInfoIndex.put(info.type, info.name); + } + configurationFile.close(); + reader.close(); + config.save(); + if (file.renameTo(new File(file.getAbsolutePath() + ".converted.to."+ TradeCraftConfigurationFile.configName +".yml"))) { + plugin.log.info("Converted old config to new style and renamed the old config file"); + } else { + plugin.log.severe("FAILED to convert old config to new style"); + } + + } catch (IOException e) { + plugin.log.severe("Error reading " + file.getAbsolutePath()); + } + } else { + try { + config.load(); + } catch (IOException e) { + plugin.log.severe("Error loading plugin config file"); + } + } + + Iterator> iter = config.getValues(false).entrySet().iterator(); + while (iter.hasNext()) { + // store map of lowercase item names to key names in the configuration + Map.Entry entry = (Map.Entry)iter.next(); + this.mapItemNames.put(entry.getKey().toLowerCase(), entry.getKey()); + // store map of item types to key names + MemorySection section = (MemorySection) entry.getValue(); + TradeCraftItem tcItem = new TradeCraftItem(section.getInt("itemTypeId", 266), section.getInt("itemTypeData", 0)); + TCitemInfoIndex.put(tcItem, entry.getKey()); + } + } + + public String[] getNames() { + String[] names = this.getConfig().getKeys(false).toArray(new String[0]); + Arrays.sort(names); + return names; + } + + public boolean isConfigured(String name) { + return this.mapItemNames.containsKey(name.toLowerCase()); + } + + public TradeCraftConfigurationInfo get(String name) { + + // @todo, config code doesn't seem to like serialized objects, cannot seem to find the TradeCraftConfigurationInfo class + // though, the otherwise resulting ==: classpath lines aren't very user friendly anyway. +// return (TradeCraftConfigurationInfo) plugin.getConfig().get(name.toUpperCase()); + String itemName = this.mapItemNames.get(name.toLowerCase()); + if (null != itemName) { + MemorySection item = ((MemorySection)this.getConfig().get(itemName)); + if (null != item) { + Map itemData = item.getValues(false); + if (null != itemData) { + return new TradeCraftConfigurationInfo(itemData, name); + } + } + } + return null; + } + public TradeCraftConfigurationInfo get(int id) { + return this.get(new TradeCraftItem(id)); + } + public TradeCraftConfigurationInfo get(int id, short data) { + return this.get(new TradeCraftItem(id, data)); + } + public TradeCraftConfigurationInfo get(TradeCraftItem item) { + if (!TCitemInfoIndex.containsKey(item)) { + return null; + } + return this.get(TCitemInfoIndex.get(item)); + } +} \ No newline at end of file diff --git a/src/nl/armeagle/TradeCraft/TradeCraftConfigurationInfo.java b/src/nl/armeagle/TradeCraft/TradeCraftConfigurationInfo.java new file mode 100644 index 0000000..7b62336 --- /dev/null +++ b/src/nl/armeagle/TradeCraft/TradeCraftConfigurationInfo.java @@ -0,0 +1,55 @@ +package nl.armeagle.TradeCraft; + +import java.util.LinkedHashMap; +import java.util.Map; +import org.bukkit.configuration.serialization.ConfigurationSerializable; +import org.bukkit.configuration.serialization.SerializableAs; + +@SerializableAs("nl.armeagle.TradeCraft.TradeCraftConfigurationInfo") +class TradeCraftConfigurationInfo implements ConfigurationSerializable { + public String name; + public TradeCraftItem type; + public int buyAmount; + public int buyValue; + public int sellAmount; + public int sellValue; + + TradeCraftConfigurationInfo() { + } + + TradeCraftConfigurationInfo(Map map, String name) { + this.name = name; + this.type = new TradeCraftItem((Integer) map.get("itemTypeId"), (Integer) map.get("itemTypeData")); + this.buyAmount = (Integer) map.get("buyAmount"); + this.buyValue = (Integer) map.get("buyValue"); + this.sellAmount = (Integer) map.get("sellAmount"); + this.sellValue = (Integer) map.get("sellValue"); + } + TradeCraftConfigurationInfo(Map map) { + this.name = (String) map.get("name"); + this.type = new TradeCraftItem((Integer) map.get("itemTypeId"), (Integer) map.get("itemTypeData")); + this.buyAmount = (Integer) map.get("buyAmount"); + this.buyValue = (Integer) map.get("buyValue"); + this.sellAmount = (Integer) map.get("sellAmount"); + this.sellValue = (Integer) map.get("sellValue"); + } + + public Map toMap() { + LinkedHashMap map = new LinkedHashMap(); + map.put("name", this.name); + map.put("itemTypeId", this.type.id); + map.put("itemTypeData", this.type.data); + map.put("buyAmount", this.buyAmount); + map.put("buyValue", this.buyValue); + map.put("sellAmount", this.sellAmount); + map.put("sellValue", this.sellValue); + return map; + } + @Override + public Map serialize() { + return this.toMap(); + } + public static TradeCraftConfigurationInfo deserialize(Map map) { + return new TradeCraftConfigurationInfo(map); + } +} \ No newline at end of file diff --git a/src/nl/armeagle/TradeCraft/TradeCraftDataFile.java b/src/nl/armeagle/TradeCraft/TradeCraftDataFile.java new file mode 100644 index 0000000..29f7dbd --- /dev/null +++ b/src/nl/armeagle/TradeCraft/TradeCraftDataFile.java @@ -0,0 +1,360 @@ +package nl.armeagle.TradeCraft; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.block.Sign; +import org.bukkit.entity.Player; + +class TradeCraftDataFile { + /* + * As of version 1.0.5 there is support for multiple worlds. + * Newly created shops will add the world name to the information stored. + * Old shops will be converted when first interacted with. + */ + + private static final String fileName = "plugins" + File.separator + TradeCraft.pluginName+ File.separator + TradeCraft.pluginName + ".data"; + private static final Pattern infoPatternNoWorld = Pattern.compile( + "^\\s*([^,]+)\\s*," + // ownerName + "\\s*(-?\\d+)\\s*,\\s*(-?\\d+)\\s*,\\s*(-?\\d+)\\s*," + // x,y,z + "\\s*(\\d+(?:;\\d+)?)\\s*," + // itemType[!data] + "\\s*(\\d+)\\s*," + // itemAmount + "\\s*(\\d+)\\s*$"); // currencyAmount + private static final Pattern infoPatternWorld = Pattern.compile( + "^\\s*([^,]+)\\s*," + // ownerName + "\\s*([^,]+)\\s*," + // world name + "\\s*(-?\\d+)\\s*,\\s*(-?\\d+)\\s*,\\s*(-?\\d+)\\s*," + // x,y,z + "\\s*(\\d+(?:;\\d+)?)\\s*," + // itemType[!data] + "\\s*(\\d+)\\s*," + // itemAmount + "\\s*(\\d+)\\s*$"); // currencyAmount + + private final TradeCraft plugin; + private final Map data = new HashMap(); + private final File dFile = new File(fileName); + public boolean wasLoaded = false; + + + TradeCraftDataFile(TradeCraft plugin) { + this.plugin = plugin; + } + + public void load() { + try { + dFile.createNewFile(); + data.clear(); + + BufferedReader reader = new BufferedReader(new FileReader(fileName)); + + String line; + int lineNumber = 0; + + while ((line = reader.readLine()) != null) { + lineNumber += 1; + + String ownerName; + String worldName; + int x; + int y; + int z; + int itemAmount; + int currencyAmount; + String itemIdData; + + Matcher infoMatcher2 = infoPatternNoWorld.matcher(line); + + if (infoMatcher2.matches()) { + ownerName = infoMatcher2.group(1); + worldName = null; + x = Integer.parseInt(infoMatcher2.group(2)); + y = Integer.parseInt(infoMatcher2.group(3)); + z = Integer.parseInt(infoMatcher2.group(4)); + itemIdData = infoMatcher2.group(5); + itemAmount = Integer.parseInt(infoMatcher2.group(6)); + currencyAmount = Integer.parseInt(infoMatcher2.group(7)); + } else { + // support for multiple worlds + Matcher infoMatcher3 = infoPatternWorld.matcher(line); + if ( !infoMatcher3.matches()) { + plugin.log.warning( + "Failed to parse line number " + lineNumber + + " in " + fileName + + ": " + line); + continue; + } + + ownerName = infoMatcher3.group(1); + worldName = infoMatcher3.group(2); + x = Integer.parseInt(infoMatcher3.group(3)); + y = Integer.parseInt(infoMatcher3.group(4)); + z = Integer.parseInt(infoMatcher3.group(5)); + itemIdData = infoMatcher3.group(6); + itemAmount = Integer.parseInt(infoMatcher3.group(7)); + currencyAmount = Integer.parseInt(infoMatcher3.group(8)); + } + + TradeCraftDataInfo info = new TradeCraftDataInfo(); + info.ownerName = ownerName; + info.worldName = worldName; + info.itemAmount = itemAmount; + info.currencyAmount = currencyAmount; + + String key = getKey(worldName, x, y, z); + data.put(key, info); + + Matcher IdSplitData = TradeCraft.itemPatternIdSplitData.matcher(itemIdData); + + if ( IdSplitData.matches() ) { + int itemId = Integer.parseInt(IdSplitData.group(1)); + if ( IdSplitData.group(2) != null ) { + info.itemType = new TradeCraftItem(itemId, Short.parseShort(IdSplitData.group(2))); + } else { + info.itemType = new TradeCraftItem(itemId); + } + } else { + plugin.log.warning( + "Failed to parse line number " + lineNumber + + " in " + fileName + + ": " + line); + continue; + } + } + + plugin.log.info("Loaded " + data.size() + " shops"); + reader.close(); + } catch (IOException e) { + plugin.log.warning("Error reading " + fileName); + } + this.wasLoaded = true; + } + + public void save() { + if ( ! this.wasLoaded ) { + this.plugin.log.severe("TradeCraft: failed to load data file when plugin was enabled, will not save to prevent loss of items."); + // The failure should have been such that no interaction with shops would have been possible, so no items should have been lost since the plugin was + // loaded till this save point. TODO, make sure that no items are lost, when save is actually called after motations, even though that situation + // should never possibly occur. + return; + } + try { + BufferedWriter writer = new BufferedWriter(new FileWriter(fileName)); + + for (String key : data.keySet()) { + TradeCraftDataInfo info = data.get(key); + writer.write(info.ownerName + "," + + key + "," + + info.itemType.toShortString() + "," + + info.itemAmount + "," + + info.currencyAmount); + writer.newLine(); +// plugin.getServer().broadcastMessage(info.ownerName + "," + +// key + "," + +// info.itemType + "," + +// info.itemAmount + "," + +// info.currencyAmount); + } + + writer.close(); + } catch (IOException e) { + plugin.log.warning("Error writing " + fileName); + } + } + + public void deleteShop(TradeCraftShop shop){ + Location l = shop.sign.getBlock().getLocation(); + String key = getKey(shop.sign.getWorld().getName(), l.getBlockX(),l.getBlockY(),l.getBlockZ()); + if(data.containsKey(key)){ + data.remove(key); + save(); + } + } + + public Map shopsOwned(String playerName){ + Map list = new HashMap(); + for (String key : data.keySet()) { + TradeCraftDataInfo info = data.get(key); + if(info.ownerName.equalsIgnoreCase(playerName)){ + list.put(key, info); + } + } + + return list; + } + + public void setOwnerOfSign(String ownerName, Sign sign) { + depositCurrency(ownerName, sign, 0); + } + + public String getOwnerOfSign(Sign sign) { + String key = getKeyFromSign(sign); + if (data.containsKey(key)) { + TradeCraftDataInfo info = data.get(key); + return info.ownerName; + } + return null; + } + + public int getItemAmount(Sign sign) { + String key = getKeyFromSign(sign); + if (data.containsKey(key)) { + TradeCraftDataInfo info = data.get(key); + return info.itemAmount; + } + return 0; + } + + public int getCurrencyAmount(Sign sign) { + String key = getKeyFromSign(sign); + if (data.containsKey(key)) { + TradeCraftDataInfo info = data.get(key); + return info.currencyAmount; + } + return 0; + } + + public void depositItems(String ownerName, Sign sign, TradeCraftItem itemType, int itemAmount) { + TradeCraftDataInfo info; + String key = getKeyFromSign(sign); + + if (data.containsKey(key)) { + info = data.get(key); + } else { + info = new TradeCraftDataInfo(); + } + + info.ownerName = ownerName; + info.worldName = sign.getWorld().getName(); + info.itemType = itemType; + info.itemAmount += itemAmount; + data.put(key, info); + + save(); + } + + public void depositCurrency(String ownerName, Sign sign, int currencyAmount) { + TradeCraftDataInfo info; + String key = getKeyFromSign(sign); + + if (data.containsKey(key)) { + info = data.get(key); + } else { + info = new TradeCraftDataInfo(); + } + + info.ownerName = ownerName; + info.worldName = sign.getWorld().getName(); + info.currencyAmount += currencyAmount; + data.put(key, info); + + save(); + } + + public int withdrawItems(Sign sign) { + String key = getKeyFromSign(sign); + if (!data.containsKey(key)) { + return 0; + } + TradeCraftDataInfo info = data.get(key); + int itemAmount = info.itemAmount; + if (itemAmount != 0) { + info.itemAmount = 0; + save(); + } + return itemAmount; + } + + public int withdrawCurrency(Sign sign) { + String key = getKeyFromSign(sign); + if (!data.containsKey(key)) { + return 0; + } + TradeCraftDataInfo info = data.get(key); + int currencyAmount = info.currencyAmount; + if (currencyAmount != 0) { + info.currencyAmount = 0; + save(); + } + return currencyAmount; + } + + public void updateItemAndCurrencyAmounts(Sign sign, int itemAdjustment, int currencyAdjustment) { + String key = getKeyFromSign(sign); + if (!data.containsKey(key)) { + return; + } + TradeCraftDataInfo info = data.get(key); + info.itemAmount += itemAdjustment; + info.currencyAmount += currencyAdjustment; + save(); + } + + private String getKeyFromSign(Sign sign) { + String keyWithWorld = getKey(sign.getWorld().getName(), sign.getX(), sign.getY(), sign.getZ()); + // convert old style keys (without world name) to new style and return the new key + if ( !data.containsKey(keyWithWorld) ) { + // try the old style key, without the world part + String keyWithoutWorld = getKey(null, sign.getX(), sign.getY(), sign.getZ()); + if ( data.containsKey(keyWithoutWorld) ) { + TradeCraftDataInfo shopInfo = data.get(keyWithoutWorld); + data.remove(keyWithoutWorld); + data.put(keyWithWorld, shopInfo); + } + } + return keyWithWorld; + } + + private String getKey(String world, int x, int y, int z) { + // support for multiple words now, optionally accepting a world passed on. + if ( world == null ) { + return x + "," + y + "," + z; + } else { + return world + "," + x + "," + y + "," + z; + } + } + + public void createNewSign(String ownerName, TradeCraftConfigurationInfo itemInfo, Sign sign) { + TradeCraftDataInfo info; + String key = getKeyFromSign(sign); + + if (data.containsKey(key)) { + info = data.get(key); + } else { + info = new TradeCraftDataInfo(); + } + + info.ownerName = ownerName; + info.worldName = sign.getWorld().getName(); + info.currencyAmount = 0; + info.itemAmount = 0; + info.itemType = itemInfo.type; + data.put(key, info); + + save(); + } + + public int getPlayerShopCount(Player player, World world) { + Map list = this.shopsOwned(player.getName()); + int shopsOwnedCount = 0; + + for (String key : list.keySet()) { + TradeCraftDataInfo info = data.get(key); + if (info.worldName.equalsIgnoreCase(world.getName())) { + shopsOwnedCount++; + } + } + + return shopsOwnedCount; + } + public int getPlayerShopCount(Player player) { + return this.shopsOwned(player.getName()).size(); + } +} \ No newline at end of file diff --git a/src/nl/armeagle/TradeCraft/TradeCraftDataInfo.java b/src/nl/armeagle/TradeCraft/TradeCraftDataInfo.java new file mode 100644 index 0000000..e9dce65 --- /dev/null +++ b/src/nl/armeagle/TradeCraft/TradeCraftDataInfo.java @@ -0,0 +1,17 @@ +package nl.armeagle.TradeCraft; + +class TradeCraftDataInfo { + public String ownerName; + public String worldName; + public TradeCraftItem itemType; + public int itemAmount; + public int currencyAmount; + + public TradeCraftDataInfo() { + this.ownerName = null; + this.worldName = null; + this.itemType = null; + this.itemAmount = 0; + this.currencyAmount = 0; + } +} \ No newline at end of file diff --git a/src/nl/armeagle/TradeCraft/TradeCraftExchangeRate.java b/src/nl/armeagle/TradeCraft/TradeCraftExchangeRate.java new file mode 100644 index 0000000..5fd3ae0 --- /dev/null +++ b/src/nl/armeagle/TradeCraft/TradeCraftExchangeRate.java @@ -0,0 +1,27 @@ +package nl.armeagle.TradeCraft; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class TradeCraftExchangeRate { + public int amount; + public int value; + + private static final Pattern ratePattern = Pattern.compile("(\\d+)\\D+(\\d+)\\s*"); + + TradeCraftExchangeRate(String signLine) { + Matcher matcher = ratePattern.matcher(signLine); + + if (matcher.find()) { + this.amount = Integer.parseInt(matcher.group(1)); + this.value = Integer.parseInt(matcher.group(2)); + } else { + this.amount = 0; + this.value = 0; + } + } + + public boolean isValid() { + return this.amount != 0; + } +} diff --git a/com/mjmr89/TradeCraft/TradeCraftInfiniteShop.java b/src/nl/armeagle/TradeCraft/TradeCraftInfiniteShop.java similarity index 81% rename from com/mjmr89/TradeCraft/TradeCraftInfiniteShop.java rename to src/nl/armeagle/TradeCraft/TradeCraftInfiniteShop.java index 77d1224..94786d5 100644 --- a/com/mjmr89/TradeCraft/TradeCraftInfiniteShop.java +++ b/src/nl/armeagle/TradeCraft/TradeCraftInfiniteShop.java @@ -1,4 +1,4 @@ -package com.mjmr89.TradeCraft; +package nl.armeagle.TradeCraft; import org.bukkit.block.Chest; import org.bukkit.block.Sign; @@ -7,15 +7,18 @@ public class TradeCraftInfiniteShop extends TradeCraftItemShop { private final TradeCraftConfigurationInfo configurationInfo; - public TradeCraftInfiniteShop(TradeCraft plugin, Sign sign, Chest chest) { + public TradeCraftInfiniteShop(TradeCraft plugin, Sign sign, Chest chest) throws Exception { super(plugin, sign, chest); - String itemName = plugin.getItemName(sign); + String itemName = plugin.getItemName(sign.getLines()); configurationInfo = plugin.configuration.get(itemName); + if (null == configurationInfo) { + throw new Exception("Invalid item name on sign: "+ itemName); + } } public boolean playerCanDestroy(Player player) { - return plugin.permissions.canDestroyShops(player); + return plugin.permissions.canDestroyShops(player); } public boolean shopCanBeWithdrawnFrom() { @@ -26,8 +29,8 @@ public boolean isOwnedByPlayer(Player player) { return false; } - public int getItemType() { - return configurationInfo.id; + public TradeCraftItem getItemType() { + return configurationInfo.type; } public String getItemName() { diff --git a/src/nl/armeagle/TradeCraft/TradeCraftItem.java b/src/nl/armeagle/TradeCraft/TradeCraftItem.java new file mode 100644 index 0000000..9bb32f5 --- /dev/null +++ b/src/nl/armeagle/TradeCraft/TradeCraftItem.java @@ -0,0 +1,63 @@ +package nl.armeagle.TradeCraft; + +/** + * + * @author ArmEagle + * Store item type(ID) and optionally the data bit + */ +public class TradeCraftItem implements Comparable { + public int id; + public short data; + + TradeCraftItem(int id) { + this(id, (short)0); + } + TradeCraftItem(int id, int data) { + this(id, new Integer(data).shortValue()); + } + TradeCraftItem(int id, short data) { + this.id = id; + this.data = data; + } + + /** + * @param compare + * @throws NullPointerException if compare is null + * @return default < 0 > compare values + */ + @Override public int compareTo(TradeCraftItem compare) { + if ( this == compare ) { + return 0; + } + if ( this.id < compare.id ) { + return -1; + } else if ( this.id > compare.id ) { + return 1; + } else { + if ( this.data < compare.data ) { + return -1; + } else if ( this.data > compare.data ) { + return 1; + } else { + return 0; + } + } + } + @Override public boolean equals(Object compare) { + return (compare == null ? false : (compare instanceof TradeCraftItem? this.compareTo((TradeCraftItem)compare) == 0 : false)); + } + @Override public int hashCode() { + return this.id * 32768 + this.data; + } + + @Override public String toString() { + return "TradeCraftItem("+ this.id +";"+ this.data +")"; + } + public String toShortString() { + if ( this.data == 0 ) { + return String.valueOf(this.id); + } else { + return this.id +";"+ this.data; + } + } +} diff --git a/src/nl/armeagle/TradeCraft/TradeCraftItemShop.java b/src/nl/armeagle/TradeCraft/TradeCraftItemShop.java new file mode 100644 index 0000000..ad65463 --- /dev/null +++ b/src/nl/armeagle/TradeCraft/TradeCraftItemShop.java @@ -0,0 +1,399 @@ +package nl.armeagle.TradeCraft; + +import org.bukkit.block.Chest; +import org.bukkit.block.Sign; +import org.bukkit.entity.Player; + +public abstract class TradeCraftItemShop extends TradeCraftShop { + + public TradeCraftItemShop(TradeCraft plugin, Sign sign, Chest chest) { + super(plugin, sign, chest); + } + + public void handleRightClick(Player player) { + if (isOwnedByPlayer(player)) { + handleOwnerClick(player); + } else { + handlePatronClick(player); + } + } + + private void handleOwnerClick(Player player) { + if (!chestContentsAreOK()) { + plugin.sendMessage(player, TradeCraftLocalization.get("THE_CHEST_HAS_MORE_THAN_ONE_TYPE")); + return; + } + + if (getChestItemCount() == 0) { + int currencyAmount = withdrawCurrency(); + if (currencyAmount > 0) { + // limit amount of currency dropped into the chest to the max amount it can hold + int maxCurrencyChestCanHold = chest.getSize() * TradeCraft.getMaxStackSize(TradeCraft.currency.id); + if ( currencyAmount > maxCurrencyChestCanHold ) { + populateChest(TradeCraft.currency, maxCurrencyChestCanHold); // fill to the max + depositCurrency(currencyAmount - maxCurrencyChestCanHold); // return remaining money back to shop data + plugin.sendMessage(player, TradeCraft.MessageTypes.WITHDRAW, + TradeCraftLocalization.get("WITHDREW_X_CURRENCY_SHOP_STILL_HOLDS_Y_CURRENCY"), + maxCurrencyChestCanHold, + plugin.getCurrencyName(), + currencyAmount - maxCurrencyChestCanHold); + plugin.useLog(player, this, + TradeCraftLocalization.get("WITHDREW_X_CURRENCY_SHOP_STILL_HOLDS_Y_CURRENCY"), + maxCurrencyChestCanHold, + plugin.getCurrencyName(), + currencyAmount - maxCurrencyChestCanHold); + } else { + populateChest(TradeCraft.currency, currencyAmount); + plugin.sendMessage(player, TradeCraft.MessageTypes.WITHDRAW, + TradeCraftLocalization.get("WITHDREW_X_A"), + currencyAmount, + plugin.getCurrencyName()); + plugin.useLog(player, this, + TradeCraftLocalization.get("WITHDREW_X_A"), + currencyAmount, + plugin.getCurrencyName()); + } + } else { + // limit amount of items dropped into the chest + int itemAmount = withdrawItems(); + if (itemAmount > 0) { + int maxItemsChestCanHold = chest.getSize() * TradeCraft.getMaxStackSize(getItemType().id); + if ( itemAmount > maxItemsChestCanHold ) { + populateChest(getItemType(), maxItemsChestCanHold); + depositItems(itemAmount - maxItemsChestCanHold); + plugin.sendMessage(player, TradeCraft.MessageTypes.WITHDRAW, + TradeCraftLocalization.get("WITHDREW_X_A_SHOP_STILL_HOLDS_Y_A"), + maxItemsChestCanHold, + getItemName(), + itemAmount - maxItemsChestCanHold); + plugin.useLog(player, this, + TradeCraftLocalization.get("WITHDREW_X_A_SHOP_STILL_HOLDS_Y_A"), + maxItemsChestCanHold, + getItemName(), + itemAmount - maxItemsChestCanHold); + } else { + populateChest(getItemType(), itemAmount); + plugin.sendMessage(player, TradeCraft.MessageTypes.WITHDRAW, + TradeCraftLocalization.get("WITHDREW_X_A"), + itemAmount, + getItemName()); + plugin.useLog(player, this, + TradeCraftLocalization.get("WITHDREW_X_A"), + itemAmount, + getItemName()); + } + } else { + plugin.sendMessage(player, TradeCraftLocalization.get("THERE_IS_NOTHING_TO_WITHDRAW")); + } + } + } else if ( getChestItemType().compareTo(TradeCraft.currency) == 0 ) { + depositCurrency(getChestItemCount()); + plugin.sendMessage(player, TradeCraft.MessageTypes.DEPOSIT, + TradeCraftLocalization.get("DEPOSITED_X_A"), + getChestItemCount(), + plugin.getCurrencyName()); + plugin.useLog(player, this, + TradeCraftLocalization.get("DEPOSITED_X_A"), + getChestItemCount(), + plugin.getCurrencyName()); + populateChest(new TradeCraftItem(0), 0); + int itemAmount = withdrawItems(); + if (itemAmount > 0) { + int maxItemsChestCanHold = chest.getSize() * TradeCraft.getMaxStackSize(getChestItemType().id); + if ( itemAmount > maxItemsChestCanHold ) { + populateChest(getItemType(), maxItemsChestCanHold); + depositItems(itemAmount - maxItemsChestCanHold); + plugin.sendMessage(player, TradeCraft.MessageTypes.WITHDRAW, + TradeCraftLocalization.get("WITHDREW_X_A_SHOP_STILL_HOLDS_Y_A"), + maxItemsChestCanHold, + getItemName(), + itemAmount - maxItemsChestCanHold); + plugin.useLog(player, this, + TradeCraftLocalization.get("WITHDREW_X_A_SHOP_STILL_HOLDS_Y_A"), + maxItemsChestCanHold, + getItemName(), + itemAmount - maxItemsChestCanHold); + } else { + populateChest(getItemType(), itemAmount); + plugin.sendMessage(player, TradeCraft.MessageTypes.WITHDRAW, + TradeCraftLocalization.get("WITHDREW_X_A"), + itemAmount, + getItemName()); + plugin.useLog(player, this, + TradeCraftLocalization.get("WITHDREW_X_A"), + itemAmount, + getItemName()); + } + } + } else if ( getChestItemType().compareTo(getItemType()) == 0 ) { + depositItems(getChestItemCount()); + populateChest(new TradeCraftItem(0), 0); + plugin.sendMessage(player, TradeCraft.MessageTypes.DEPOSIT, + TradeCraftLocalization.get("DEPOSITED_X_A"), + getChestItemCount(), + getItemName()); + plugin.useLog(player, this, + TradeCraftLocalization.get("DEPOSITED_X_A"), + getChestItemCount(), + getItemName()); + } else { + plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_DEPOSIT_THAT_HERE")); + } + } + + private void handlePatronClick(Player player) { + + + boolean playerCanBuy= (plugin.permissions.canBuy(player)); + boolean playerCanSell = plugin.permissions.canSell(player); + + getChestItemCount(); + + if (!chestContentsAreOK()) { + plugin.sendMessage(player, TradeCraftLocalization.get("THE_CHEST_HAS_MORE_THAN_ONE_TYPE")); + return; + } + + if (getChestItemCount() == 0) { + if (playerCanBuy && playerCanBuy()) { + if ( this instanceof TradeCraftInfiniteShop ) { + plugin.sendMessage(player, + TradeCraftLocalization.get("YOU_CAN_BUY_Y_A_FOR_X_B"), + getBuyAmount(), + getItemName(), + getBuyValue(), + plugin.getCurrencyName()); + } else { + plugin.sendMessage(player, + TradeCraftLocalization.get("YOU_CAN_BUY_X_A_FOR_Y_B_UP_TO_Z"), + getBuyAmount(), + getItemName(), + getBuyValue(), + plugin.getCurrencyName(), + this.getItemsInShop()); + } + } + + if (playerCanSell && playerCanSell()) { + if ( this instanceof TradeCraftInfiniteShop ) { + plugin.sendMessage(player, + TradeCraftLocalization.get("YOU_CAN_SELL_X_A_FOR_Y_B"), + getSellAmount(), + getItemName(), + getSellValue(), + plugin.getCurrencyName()); + } else { + plugin.sendMessage(player, + TradeCraftLocalization.get("YOU_CAN_SELL_X_A_FOR_Y_B_UP_TO_Z"), + getSellAmount(), + getItemName(), + getSellValue(), + plugin.getCurrencyName(), + this.getCurrencyInShop()); + } + } + + if ( this instanceof TradeCraftInfiniteShop ) { + plugin.sendMessage(player, TradeCraftLocalization.get("THIS_IS_AN_INFINITE_SHOP")); + } + return; + } + + plugin.trace(player, "%s %s %s", getChestItemType(), TradeCraft.currency, getItemType()); + if ( getChestItemType().compareTo(TradeCraft.currency) == 0 ) { + if (!playerCanBuy) { + plugin.sendMessage(player, TradeCraftLocalization.get("YOU_ARE_NOT_ALLOWED_TO_BUY")); + } else { + playerWantsToBuy(player); + } + } else if ( getChestItemType().compareTo(getItemType()) == 0 ) { + if (!playerCanSell) { + plugin.sendMessage(player, TradeCraftLocalization.get("YOU_ARE_NO_ALLOWED_TO_SELL")); + } else { + playerWantsToSell(player); + } + } else { + plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_SELL_THAT")); + } + } + + private void playerWantsToBuy(Player player) { + if (!playerCanBuy()) { + plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_BUY_HERE")); + return; + } + + int currencyPlayerWantsToSpend = getChestItemCount(); + int amountPlayerWantsToBuy = ((currencyPlayerWantsToSpend - (currencyPlayerWantsToSpend % getBuyValue()) ) / getBuyValue()) * getBuyAmount(); + if ( getBuyAmount() > this.chest.getSize() * TradeCraft.getMaxStackSize(getItemType().id) ) { + plugin.sendMessage(player, TradeCraftLocalization.get("THIS_SHOP_ALWAYS_RETURNS_TOO_MUCH")); + return; + } + + if (amountPlayerWantsToBuy == 0) { + plugin.sendMessage(player, + TradeCraftLocalization.get("YOU_NEED_TO_SPEND_AT_LEAST_X_A_TO_GET_ANY_B"), + getBuyValue(), + plugin.getCurrencyName(), + getItemName()); + return; + } + + if (amountPlayerWantsToBuy > getItemsInShop()) { + if ( getItemsInShop() == 0 ) { + plugin.sendMessage(player, + TradeCraftLocalization.get("CANT_BUY_SHOP_HAS_NO_A_LEFT"), + getItemName()); + } else { + plugin.sendMessage(player, + TradeCraftLocalization.get("CANNOT_BUY_SHOP_ONLY_HAS_X_A"), + getItemsInShop(), + getItemName()); + } + return; + } + + int requiredCurrencyForThatAmount = amountPlayerWantsToBuy * getBuyValue() / getBuyAmount(); + + if ( Math.ceil( (currencyPlayerWantsToSpend - requiredCurrencyForThatAmount) / TradeCraft.getMaxStackSize(TradeCraft.currency.id)) + + Math.ceil( amountPlayerWantsToBuy / TradeCraft.getMaxStackSize(getItemType().id)) + > this.chest.getSize() ) { + plugin.sendMessage(player, TradeCraftLocalization.get("THIS_SHOP_WOULD_RETURN_TOO_MANY_BUY_LESS")); + return; + } + + updateItemAndCurrencyAmounts(-amountPlayerWantsToBuy, requiredCurrencyForThatAmount); + + chest.clear(); + chest.add(TradeCraft.currency, currencyPlayerWantsToSpend - requiredCurrencyForThatAmount); + chest.add(getItemType(), amountPlayerWantsToBuy); + chest.update(); + + plugin.sendMessage(player, + TradeCraftLocalization.get("YOU_BOUGHT_X_A_FOR_Y_B"), + amountPlayerWantsToBuy, + getItemName(), + requiredCurrencyForThatAmount, + plugin.getCurrencyName()); + plugin.useLog(player, this, + TradeCraftLocalization.get("BOUGHT_X_A_FOR_Y_B"), + amountPlayerWantsToBuy, + getItemName(), + requiredCurrencyForThatAmount, + plugin.getCurrencyName()); + } + + private void playerWantsToSell(Player player) { + if (!playerCanSell()) { + plugin.sendMessage(player, TradeCraftLocalization.get("YOU_CANT_SELL_THAT")); + return; + } + + int amountPlayerWantsToSell = getChestItemCount(); + int currencyPlayerShouldReceive = ((amountPlayerWantsToSell - (amountPlayerWantsToSell % getSellAmount())) / getSellAmount()) * getSellValue(); + // prevent too much currency (more than can fit in a chest) to be given to the customer + if ( getSellValue() > this.chest.getSize() * TradeCraft.getMaxStackSize(TradeCraft.currency.id) ) { + plugin.sendMessage(player, TradeCraftLocalization.get("THIS_SHOP_ALWAYS_RETURNS_TOO_MUCH_CURRENCY"), this.plugin.getCurrencyName()); + return; + } + + if (currencyPlayerShouldReceive == 0) { + plugin.sendMessage(player, + TradeCraftLocalization.get("YOU_NEED_TO_SELL_AT_LEAST_X_A_TO_GET_ANY_B"), + getSellAmount(), + getItemName(), + plugin.getCurrencyName()); + return; + } + + if (currencyPlayerShouldReceive > getCurrencyInShop()) { + plugin.sendMessage(player, + TradeCraftLocalization.get("CANNOT_SELL_SHOP_ONLY_HAS_X_A"), + getCurrencyInShop(), + plugin.getCurrencyName()); + return; + } + + int amountThatCanBeSold = currencyPlayerShouldReceive * getSellAmount() / getSellValue(); + + // prevent too much items+currency stacks to end up in the chest + if ( Math.ceil( (amountPlayerWantsToSell - amountThatCanBeSold)/ TradeCraft.getMaxStackSize(getItemType().id) ) + + Math.ceil( currencyPlayerShouldReceive / TradeCraft.getMaxStackSize(TradeCraft.currency.id) ) + > chest.getSize() ) { + plugin.sendMessage(player, TradeCraftLocalization.get("THIS_SHOP_WOULD_RETURN_TOO_MUCH_CURRENCY_SELL_LESS"), this.plugin.getCurrencyName()); + return; + } + + updateItemAndCurrencyAmounts(amountThatCanBeSold, -currencyPlayerShouldReceive); + + chest.clear(); + chest.add(getItemType(), amountPlayerWantsToSell - amountThatCanBeSold); + chest.add(TradeCraft.currency, currencyPlayerShouldReceive); + chest.update(); + + plugin.sendMessage(player, + TradeCraftLocalization.get("YOU_SOLD_X_A_FOR_Y_B"), + amountThatCanBeSold, + getItemName(), + currencyPlayerShouldReceive, + plugin.getCurrencyName()); + plugin.useLog(player, this, + TradeCraftLocalization.get("SOLD_X_A_FOR_Y_B"), + amountThatCanBeSold, + getItemName(), + currencyPlayerShouldReceive, + plugin.getCurrencyName()); + } + + /** + * Get the item type of the items that are currently in the chest. + * @return + */ + public TradeCraftItem getChestItemType() { + return chest.type; + } + + public int getChestItemCount() { + return chest.total; + } + + public boolean chestContentsAreOK() { + return chest.containsOnlyOneItemType(); + } + + public void populateChest(TradeCraftItem type, int amount) { + chest.populateChest(type, amount); + } + + public abstract boolean isOwnedByPlayer(Player player); + + public abstract TradeCraftItem getItemType(); + + public abstract String getItemName(); + + public abstract boolean playerCanBuy(); + + public abstract boolean playerCanSell(); + + public abstract int getBuyAmount(); + + public abstract int getBuyValue(); + + public abstract int getSellAmount(); + + public abstract int getSellValue(); + + public abstract int getItemsInShop(); + + public abstract int getCurrencyInShop(); + + public abstract void depositItems(int amount); + + public abstract void depositCurrency(int amount); + + public abstract int withdrawItems(); + + public abstract int withdrawCurrency(); + + public abstract void updateItemAndCurrencyAmounts(int itemAdjustment, int CurrencyAdjustment); +} \ No newline at end of file diff --git a/src/nl/armeagle/TradeCraft/TradeCraftLocalization.java b/src/nl/armeagle/TradeCraft/TradeCraftLocalization.java new file mode 100644 index 0000000..a85f769 --- /dev/null +++ b/src/nl/armeagle/TradeCraft/TradeCraftLocalization.java @@ -0,0 +1,52 @@ +package nl.armeagle.TradeCraft; + +import java.io.IOException; +import java.util.logging.Level; +import nl.armeagle.Configuration.StatefulYamlConfiguration; + +/** + * + * @author ArmEagle + * Localization support. + * + * You can put different language files in the plugins/TradeCraft/ folder, similar to the default TradeCraft.en.lang. + * There is a "language" option in the TradeCraft.properties file (default: "en") with which you can then select + * such other language file to be used. + */ +public class TradeCraftLocalization { + private static final String filePreName = TradeCraft.pluginName + ".%1$s.lang"; + private static StatefulYamlConfiguration localization; + + public TradeCraftLocalization(TradeCraft plugin) { + String filename = String.format(TradeCraftLocalization.filePreName, TradeCraft.properties.getLanguage()); + TradeCraftLocalization.localization = plugin.getConfig(filename); + + try { + TradeCraftLocalization.localization.load(); + } catch (IOException e) { + plugin.log(Level.SEVERE, "Failed to read file: %s", filename); + } + + String defaultFilename = String.format(TradeCraftLocalization.filePreName, "en"); + if (TradeCraftLocalization.localization.getKeys(false).isEmpty()) { + plugin.log(Level.INFO, "Language file %s does not exist or is empty, defaulting to %s", filename, defaultFilename); + } else { + return; + } + + TradeCraftLocalization.localization = plugin.getConfig(defaultFilename); + try { + TradeCraftLocalization.localization.load(); + } catch (IOException e) { + plugin.log(Level.SEVERE, "Failed to read file: %s", defaultFilename); + } + + if (TradeCraftLocalization.localization.getKeys(false).isEmpty()) { + plugin.log(Level.SEVERE, "Default language file %s is also empty", defaultFilename); + } + } + + public static String get(String key) { + return TradeCraftLocalization.localization.getString(key, "key error \""+ key +"\""); + } +} diff --git a/src/nl/armeagle/TradeCraft/TradeCraftPermissions.java b/src/nl/armeagle/TradeCraft/TradeCraftPermissions.java new file mode 100644 index 0000000..2471728 --- /dev/null +++ b/src/nl/armeagle/TradeCraft/TradeCraftPermissions.java @@ -0,0 +1,68 @@ +package nl.armeagle.TradeCraft; + +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; + +public class TradeCraftPermissions { + TradeCraft plugin; + + TradeCraftPermissions(TradeCraft plugin) { + this.plugin = plugin; + } + + public boolean canBuy(Player p) { + return p.hasPermission("TradeCraft.canBuy"); + } + + public boolean canSell(Player p) { + return p.hasPermission("TradeCraft.canSell"); + } + + public boolean canMakeInfShops(Player p) { + return p.hasPermission("TradeCraft.canMakeInfShops"); + } + + public boolean canMakePlayerShops(Player p) { + return p.hasPermission("TradeCraft.canMakePlayerShops"); + } + + public boolean canDestroyShops(Player p) { + return p.hasPermission("TradeCraft.canDestroyShops"); + } + + public boolean canSetCurrency(Player p) { + return p.hasPermission("TradeCraft.canSetCurrency"); + } + + public boolean canReload(Player p) { + return p.hasPermission("TradeCraft.canReload"); + } + + public boolean canQueryOtherShops(Player p) { + return p.hasPermission("TradeCraft.canQueryOtherShops"); + } + + public boolean canQueryPlayer(Player p) { + return p.hasPermission("TradeCraft.canQueryPlayer"); + } + + public void debug(CommandSender sender, String n){ + Player p = plugin.getServer().getPlayer(n); + if(p == null){ + plugin.getServer().broadcastMessage("/tc canPlayer used with a name of player who is not online."); + return; + } + String name = p.getName(); + sender.sendMessage("" + name + " has:"); + sender.sendMessage("canBuy " + canBuy(p)); + sender.sendMessage("canSell " + canSell(p)); + sender.sendMessage("canMakeInf " + canMakeInfShops(p)); + sender.sendMessage("canMakePersonal " + canMakePlayerShops(p)); + sender.sendMessage("canDestroy " + canDestroyShops(p)); + sender.sendMessage("canSetCurrency " + canSetCurrency(p)); + sender.sendMessage("canReload " + canReload(p)); + sender.sendMessage("canQueryOtherShops " + canQueryOtherShops(p)); + } + +} diff --git a/src/nl/armeagle/TradeCraft/TradeCraftPlayerListener.java b/src/nl/armeagle/TradeCraft/TradeCraftPlayerListener.java new file mode 100644 index 0000000..ad39122 --- /dev/null +++ b/src/nl/armeagle/TradeCraft/TradeCraftPlayerListener.java @@ -0,0 +1,69 @@ +package nl.armeagle.TradeCraft; + +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.block.Action; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.Listener; + + +public class TradeCraftPlayerListener implements Listener{ + + private TradeCraft plugin; + + TradeCraftPlayerListener(TradeCraft plugin){ + this.plugin = plugin; + } + + @EventHandler + public void onPlayerInteract(PlayerInteractEvent e) { + if ( !this.plugin.isEnabled() ) { + return; + } + + Player player = e.getPlayer(); + if ( player == null ) { + return; + } + + Block blockClicked = null; + if ( e.getAction() == Action.RIGHT_CLICK_BLOCK ) { + blockClicked = e.getClickedBlock(); + } else if ( e.getAction() == Action.RIGHT_CLICK_AIR ) { + // If player is holding a solid block and in the way of placing the block, a click air event will be fired instead. + // Look for up to two blocks away. + blockClicked = player.getTargetBlock(null, 2); + } + + if ( blockClicked != null ) { + TradeCraftShop shop = plugin.getShopFromSignBlock(player, blockClicked); + + if (shop == null) { + return; + } + + shop.handleRightClick(player); + e.setCancelled(true); + } + } + + @SuppressWarnings("unused") + private void displayItems(Player player) { + String[] names = plugin.configuration.getNames(); + StringBuilder sb = new StringBuilder(); + for (String name : names) { + if (sb.length() + name.length() > 60) { + plugin.sendMessage(player, sb.toString()); + sb = new StringBuilder(); + } + if (sb.length() > 0) { + sb.append(", "); + } + sb.append(name); + } + if (sb.length() > 0) { + plugin.sendMessage(player, sb.toString()); + } + } +} diff --git a/com/mjmr89/TradeCraft/TradeCraftPlayerOwnedShop.java b/src/nl/armeagle/TradeCraft/TradeCraftPlayerOwnedShop.java similarity index 77% rename from com/mjmr89/TradeCraft/TradeCraftPlayerOwnedShop.java rename to src/nl/armeagle/TradeCraft/TradeCraftPlayerOwnedShop.java index 4a20303..69ecd5e 100644 --- a/com/mjmr89/TradeCraft/TradeCraftPlayerOwnedShop.java +++ b/src/nl/armeagle/TradeCraft/TradeCraftPlayerOwnedShop.java @@ -1,4 +1,4 @@ -package com.mjmr89.TradeCraft; +package nl.armeagle.TradeCraft; import org.bukkit.block.Chest; import org.bukkit.block.Sign; @@ -7,22 +7,18 @@ public class TradeCraftPlayerOwnedShop extends TradeCraftItemShop { private final String ownerName; private final String itemName; - private final int itemType; + private final TradeCraftItem itemType; private final TradeCraftExchangeRate buyRate; private final TradeCraftExchangeRate sellRate; public TradeCraftPlayerOwnedShop(TradeCraft plugin, Sign sign, Chest chest, String ownerName) { super(plugin, sign, chest); -// ownerName = plugin.data.getOwnerOfSign(sign); this.ownerName = ownerName; - itemName = plugin.getItemName(sign); - itemType = plugin.configuration.get(itemName).id; - buyRate = plugin.getExchangeRate(sign, 1); - sellRate = plugin.getExchangeRate(sign, 2); - -// plugin.getServer().broadcastMessage("owner" + ownerName + " itemname " + itemName + -// " item type " + itemType + " buyrate " + buyRate + " sell rate" + sellRate); + this.itemName = plugin.getItemName(sign.getLines()); + this.itemType = plugin.configuration.get(this.itemName).type; + this.buyRate = new TradeCraftExchangeRate(sign.getLine(1)); + this.sellRate = new TradeCraftExchangeRate(sign.getLine(2)); } public boolean playerCanDestroy(Player player) { @@ -34,10 +30,14 @@ public boolean shopCanBeWithdrawnFrom() { } public boolean isOwnedByPlayer(Player player) { - return ownerName != null && player.getName().equals(ownerName); + if ( ownerName == null ) { + return false; + } else { + return player.getName().equals(ownerName); + } } - public int getItemType() { + public TradeCraftItem getItemType() { return itemType; } diff --git a/src/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java b/src/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java new file mode 100644 index 0000000..028704d --- /dev/null +++ b/src/nl/armeagle/TradeCraft/TradeCraftPropertiesFile.java @@ -0,0 +1,153 @@ +package nl.armeagle.TradeCraft; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.logging.Level; + +import org.bukkit.ChatColor; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; + +/** + * This class, handles the actual configuration of the plugin. The TradeCraftConfiguration class + * actually handles all the items that can be used by the shops in the game. + */ +public class TradeCraftPropertiesFile { + private static final String fileName = TradeCraft.pluginName + ".properties"; + private static final String filePath = "plugins" + File.separator + TradeCraft.pluginName; + public static final String defaultLanguage = "en"; + + private TradeCraft plugin; + private final YamlConfiguration properties; + + public TradeCraftPropertiesFile(TradeCraft plugin) { + this.plugin = plugin; + // make folder in the plugins dir if it doesn't exist yet + File path = new File(filePath); + if ( !path.exists() ) { + path.mkdirs(); + } + path = null; + + // if file does not exist in this directory, copy it from the jar + File file = new File(filePath + File.separator + fileName); + if ( !file.exists() ) { + this.plugin.log.info(filePath + File.separator + fileName +" does not exist, creating..."); + InputStream input = this.getClass().getResourceAsStream("/" + fileName); + if ( input != null ) { + FileOutputStream output = null; + + try { + output = new FileOutputStream(file); + byte[] buf = new byte[8192]; + int length = 0; + while ((length = input.read(buf)) > 0) { + output.write(buf, 0, length); + } + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + if (input != null) { + input.close(); + } + } catch (IOException e) {} + + try { + if (output != null) { + output.close(); + } + } catch (IOException e) {} + } + } + } + + properties = new YamlConfiguration(); + try { + properties.load(file); + } catch (InvalidConfigurationException e) { + plugin.log(Level.SEVERE, "Failed to load file: %s", file.toURI()); + } catch (IOException e) { + plugin.log(Level.SEVERE, "Failed to read file: %s", file.toURI()); + } + } + + protected void save() { + File file = new File(filePath + File.separator + fileName); + try { + properties.save(file); + } catch (IOException e) { + this.plugin.log(Level.SEVERE, "Error saving to file: %s", file.toURI()); + } + } + + public TradeCraftItem getCurrencyType(){ + int id = properties.getInt("currency-id",266); + short data = (short)properties.getInt("currency-data",0); + return new TradeCraftItem(id, data); + } + public void setCurrencyType(TradeCraftItem item) { + properties.set("currency-id", item.id); + properties.set("currency-data", item.data); + this.save(); + } + public boolean getNormalStackSizeUsed(){ + return properties.getBoolean("normal-stack-size", true); + } + + public boolean getInfiniteShopsEnabled() { + return properties.getBoolean("infinite-shops-enabled", true); + } + + public boolean getPlayerOwnedShopsEnabled() { + return properties.getBoolean("player-owned-shops-enabled", true); + } + + public boolean getRepairShopsEnabled() { + return properties.getBoolean("repair-shops-enabled", false); + } + + public int getRepairCost() { + return properties.getInt("repair-cost", 0); + } + + public boolean getEnableDebugMessages() { + return properties.getBoolean("enable-debug-messages", false); + } + + public String getLanguage() { + return properties.getString("language", TradeCraftPropertiesFile.defaultLanguage); + } + + public boolean autoUpdateLanguageFiles() { + return properties.getBoolean("auto-update-language-files", true); + } + + public boolean logShopUse() { + return properties.getBoolean("log-shop-use", false); + } + + public boolean showShopLocation() { + return properties.getBoolean("show-shop-location", false); + } + + public int getPlayerWorldShopLimit() { + return properties.getInt("player-world-shop-limit", 5); + } + public int getPlayerTotalShopLimit() { + return properties.getInt("player-total-shop-limit", 10); + } + + public ChatColor getMessageTypeColor(TradeCraft.MessageTypes mtype) { + switch (mtype) { + case WITHDRAW: + return ChatColor.YELLOW; + case DEPOSIT: + return ChatColor.GRAY; + default: + return ChatColor.WHITE; + } + } +} diff --git a/src/nl/armeagle/TradeCraft/TradeCraftRepairShop.java b/src/nl/armeagle/TradeCraft/TradeCraftRepairShop.java new file mode 100644 index 0000000..438f769 --- /dev/null +++ b/src/nl/armeagle/TradeCraft/TradeCraftRepairShop.java @@ -0,0 +1,73 @@ +package nl.armeagle.TradeCraft; + +import java.util.List; + +import org.bukkit.block.Chest; +import org.bukkit.block.Sign; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +public class TradeCraftRepairShop extends TradeCraftShop { + + public TradeCraftRepairShop(TradeCraft plugin, Sign sign, Chest chest) { + super(plugin, sign, chest); + } + + public void handleRightClick(Player player) { + int currencyAmount = chest.getAmountOfCurrencyInChest(); + List items = chest.getNonCurrencyItems(); + int repairCost = TradeCraft.properties.getRepairCost(); + + if (currencyAmount == 0 && items.size() == 0) { + plugin.sendMessage(player, TradeCraftLocalization.get("IT_COSTS_X_A_TO_REPAIR_AN_ITEM"), + repairCost, + plugin.getCurrencyName()); + return; + } + + int actualCost = items.size() * repairCost; + + if (items.size() == 0) { + plugin.sendMessage(player, TradeCraftLocalization.get("WITH_THIS_MUCH_A_YOU_CAN_REPAIR_Y_ITEMS"), + plugin.getCurrencyName(), + currencyAmount / repairCost); + return; + } + + if (currencyAmount < actualCost) { + if (currencyAmount > 0) { + plugin.sendMessage(player, TradeCraftLocalization.get("THAT_IS_NOT_ENOUGH_A"), + plugin.getCurrencyName()); + } + plugin.sendMessage(player, TradeCraftLocalization.get("YOU_NEED_X_A_TO_REPAIR_ALL_THIS"), + actualCost, + plugin.getCurrencyName()); + return; + } + + chest.clear(); + + for (ItemStack item : items) { + short itemData = (item.getData() == null ? (short)0 : item.getDurability()); + + chest.add(new TradeCraftItem(item.getTypeId(), itemData), 1); + } + + chest.add(TradeCraft.currency, (currencyAmount - actualCost)); + + chest.update(); + + plugin.sendMessage(player, TradeCraftLocalization.get("YOU_REPAIRED_X_ITEMS_FOR_Y_B"), + items.size(), + actualCost, + plugin.getCurrencyName()); + } + + public boolean playerCanDestroy(Player player) { + return true; + } + + public boolean shopCanBeWithdrawnFrom() { + return false; + } +} diff --git a/com/mjmr89/TradeCraft/TradeCraftShop.java b/src/nl/armeagle/TradeCraft/TradeCraftShop.java similarity index 65% rename from com/mjmr89/TradeCraft/TradeCraftShop.java rename to src/nl/armeagle/TradeCraft/TradeCraftShop.java index f94b13e..f537991 100644 --- a/com/mjmr89/TradeCraft/TradeCraftShop.java +++ b/src/nl/armeagle/TradeCraft/TradeCraftShop.java @@ -1,4 +1,4 @@ -package com.mjmr89.TradeCraft; +package nl.armeagle.TradeCraft; import org.bukkit.block.Chest; import org.bukkit.block.Sign; @@ -20,4 +20,12 @@ public TradeCraftShop(TradeCraft plugin, Sign sign, Chest chest) { public abstract boolean playerCanDestroy(Player player); public abstract boolean shopCanBeWithdrawnFrom(); + + public String toString() { + return String.format("Shop(%s:%d,%d,%d)", + this.sign.getWorld().getName(), + this.sign.getX(), + this.sign.getY(), + this.sign.getZ()); + } } diff --git a/src/plugin.yml b/src/plugin.yml new file mode 100644 index 0000000..bd45a39 --- /dev/null +++ b/src/plugin.yml @@ -0,0 +1,61 @@ +name: TradeCraft +version: AE-1.4.2 +description: Setup shops with a combination of chest and sign. +author: ArmEagle +main: nl.armeagle.TradeCraft.TradeCraft +commands: + tc: + description: Show TradeCraft command help + usage: "Usage: /tc" + tchelp: + description: Show TradeCraft command help + usage: "Usage: /tchelp" + tcshops: + description: See a printout of any personal shops you have + usage: "Usage: /tcshops" + tcpshops: + description: See a printout of shops of a specific player + usage: "Usage: /tcpshops [player]" + tcgetcurrency: + description: Get the currency used + usage: "Usage: /tcgetcurrency" + tcsetcurrency: + description: Set the currency used + permission: TradeCraft.canSetCurrency + usage: "Usage: /tcsetcurrency [item id;data]" + tcreload: + description: reload the script and with that all the configuration files + permission: TradeCraft.canReload + usage: "Usage: /tcreload" + tcplayerperms: + description: Show the permissions of the given player + permission: TradeCraft.canQueryPlayer + usage: "Usage: /tcplayerperms [player]" +permissions: + TradeCraft.canBuy: + description: Permission to buy from shops. + default: true + TradeCraft.canSell: + description: Permission to sell to shops. + default: true + TradeCraft.canMakePlayerShops: + description: Permission to make normal shops + default: true + TradeCraft.canMakeInfShops: + description: Permission to make infinite shops + default: op + TradeCraft.canDestroyShops: + description: Permission to destroy (any) shop + default: op + TradeCraft.canSetCurrency: + description: Permission to set the currency item type + default: op + TradeCraft.canReload: + description: Permission to reload the configuration + default: op + TradeCraft.canQueryOtherShops: + description: Permission to list the shops of other players + default: op + TradeCraft.canQueryPlayer: + description: Permission to show the permissions of a given player + default: op \ No newline at end of file