diff --git a/Towny/pom.xml b/Towny/pom.xml index a6316a314c..220d5e400a 100644 --- a/Towny/pom.xml +++ b/Towny/pom.xml @@ -13,7 +13,7 @@ towny jar - 0.100.0.11 + 0.100.0.14 @@ -167,7 +167,7 @@ net.kyori adventure-platform-bukkit - 4.3.1 + 4.3.2 net.kyori @@ -256,7 +256,7 @@ com.github.seeseemelk MockBukkit-v1.20 - 3.56.0 + 3.58.0 test @@ -472,7 +472,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.2.2 + 3.2.3 ${skipTests} diff --git a/Towny/src/main/java/com/palmergames/bukkit/towny/TownyEconomyHandler.java b/Towny/src/main/java/com/palmergames/bukkit/towny/TownyEconomyHandler.java index 438870324d..5956c3ab8c 100644 --- a/Towny/src/main/java/com/palmergames/bukkit/towny/TownyEconomyHandler.java +++ b/Towny/src/main/java/com/palmergames/bukkit/towny/TownyEconomyHandler.java @@ -285,6 +285,8 @@ public static boolean setBalance(String accountName, double amount, World world) * @return string containing the formatted balance */ public static String getFormattedBalance(double balance) { + if (!isActive()) + return String.valueOf(balance); String formattedBalance = economy.getFormattedBalance(balance); if (formattedBalance != null) { diff --git a/Towny/src/main/java/com/palmergames/bukkit/towny/command/BaseCommand.java b/Towny/src/main/java/com/palmergames/bukkit/towny/command/BaseCommand.java index 393b05d30c..b46d0c15d0 100644 --- a/Towny/src/main/java/com/palmergames/bukkit/towny/command/BaseCommand.java +++ b/Towny/src/main/java/com/palmergames/bukkit/towny/command/BaseCommand.java @@ -1,6 +1,7 @@ package com.palmergames.bukkit.towny.command; import com.palmergames.bukkit.towny.TownyAPI; +import com.palmergames.bukkit.towny.TownyEconomyHandler; import com.palmergames.bukkit.towny.TownyUniverse; import com.palmergames.bukkit.towny.exceptions.NoPermissionException; import com.palmergames.bukkit.towny.exceptions.ResidentNPCException; @@ -390,4 +391,8 @@ public static void catchNPCResident(Resident resident) throws ResidentNPCExcepti if (resident.isNPC()) throw new ResidentNPCException(); } + + public static String prettyMoney(double cost) { + return TownyEconomyHandler.getFormattedBalance(cost); + } } diff --git a/Towny/src/main/java/com/palmergames/bukkit/towny/command/HelpMenu.java b/Towny/src/main/java/com/palmergames/bukkit/towny/command/HelpMenu.java index 128c0c6ddd..34f710ef39 100644 --- a/Towny/src/main/java/com/palmergames/bukkit/towny/command/HelpMenu.java +++ b/Towny/src/main/java/com/palmergames/bukkit/towny/command/HelpMenu.java @@ -937,6 +937,17 @@ protected MenuBuilder load() { } }, + NATION_SANCTIONTOWN { + @Override + protected MenuBuilder load() { + return new MenuBuilder("nation sanctiontown") + .add("add [town]", Translatable.of("nation_sanction_help_1")) + .add("remove [town]", Translatable.of("nation_sanction_help_2")) + .add("list", Translatable.of("nation_sanction_help_3")) + .add("list [nation]", Translatable.of("nation_sanction_help_4")); + } + }, + ALLIES_STRING { @Override protected MenuBuilder load() { diff --git a/Towny/src/main/java/com/palmergames/bukkit/towny/command/NationCommand.java b/Towny/src/main/java/com/palmergames/bukkit/towny/command/NationCommand.java index 94b7a37309..aee7927a0f 100644 --- a/Towny/src/main/java/com/palmergames/bukkit/towny/command/NationCommand.java +++ b/Towny/src/main/java/com/palmergames/bukkit/towny/command/NationCommand.java @@ -17,6 +17,8 @@ import com.palmergames.bukkit.towny.event.NationPreRemoveEnemyEvent; import com.palmergames.bukkit.towny.event.nation.NationRankAddEvent; import com.palmergames.bukkit.towny.event.nation.NationRankRemoveEvent; +import com.palmergames.bukkit.towny.event.nation.NationSanctionTownAddEvent; +import com.palmergames.bukkit.towny.event.nation.NationSanctionTownRemoveEvent; import com.palmergames.bukkit.towny.event.nation.NationSetSpawnEvent; import com.palmergames.bukkit.towny.event.nation.NationTownLeaveEvent; import com.palmergames.bukkit.towny.event.NationRemoveEnemyEvent; @@ -125,6 +127,7 @@ public class NationCommand extends BaseCommand implements CommandExecutor { "enemylist", "ally", "spawn", + "sanctiontown", "king", "leader", "bankhistory", @@ -235,6 +238,18 @@ else if (args.length == 3) if (args.length == 3) { return Collections.singletonList("-ignore"); } + case "sanctiontown": + if (args.length == 2) + return NameUtil.filterByStart(Arrays.asList("add", "remove", "list"), args[1]); + if (args.length == 3 && args[1].equalsIgnoreCase("add") || args[1].equalsIgnoreCase("remove")) + return NameUtil.filterByStart(TownyUniverse.getInstance().getTowns() + .stream() + .filter(t -> !nation.hasTown(t)) + .map(Town::getName) + .collect(Collectors.toList()), args[2]); + if (args.length == 3 && args[1].equalsIgnoreCase("list")) + return getTownyStartingWith(args[2], "n"); + break; case "add": return getTownyStartingWith(args[args.length - 1], "t"); case "kick": @@ -528,6 +543,9 @@ public void parseNationCommand(final Player player, String[] split) throws Towny checkPermOrThrow(player, PermissionNodes.TOWNY_COMMAND_NATION_KICK.getNode()); nationKick(player, StringMgmt.remFirstArg(split)); break; + case "sanctiontown": + nationSanctionTown(player, null, StringMgmt.remFirstArg(split)); + break; case "set": /* Permission test is internal*/ nationSet(player, StringMgmt.remFirstArg(split), false, null); @@ -666,7 +684,11 @@ private void parseNationJoin(Player player, String[] args) { // Check if town is town is free to join. if (!nation.isOpen()) throw new TownyException(Translatable.of("msg_err_nation_not_open", nation.getFormattedName())); - + + // Check if the town is sanctioned and not allowed to join. + if (nation.hasSanctionedTown(town)) + throw new TownyException(Translatable.of("msg_err_cannot_join_nation_sanctioned_town", nation.getName())); + if (!testTownHasEnoughResidents(town)) throw new TownyException(Translatable.of("msg_err_not_enough_residents_join_nation", town.getName())); @@ -945,8 +967,9 @@ public static void newNation(CommandSender sender, String name, Town capitalTown // If it isn't free to make a nation, send a confirmation. if (!noCharge && TownyEconomyHandler.isActive()) { // Test if they can pay. - if (!capitalTown.getAccount().canPayFromHoldings(TownySettings.getNewNationPrice())) - throw new TownyException(Translatable.of("msg_no_funds_new_nation2", TownySettings.getNewNationPrice())); + double cost = TownySettings.getNewNationPrice(); + if (!capitalTown.getAccount().canPayFromHoldings(cost)) + throw new TownyException(Translatable.of("msg_no_funds_new_nation2", cost)); final String finalName = filteredName; Confirmation.runOnAccept(() -> { @@ -959,9 +982,9 @@ public static void newNation(CommandSender sender, String name, Town capitalTown TownyMessaging.sendGlobalMessage(Translatable.of("msg_new_nation", sender.getName(), StringMgmt.remUnderscore(finalName))); }) - .setCost(new ConfirmationTransaction(TownySettings::getNewNationPrice, capitalTown.getAccount(), "New Nation Cost", - Translatable.of("msg_no_funds_new_nation2", TownySettings.getNewNationPrice()))) - .setTitle(Translatable.of("msg_confirm_purchase", TownyEconomyHandler.getFormattedBalance(TownySettings.getNewNationPrice()))) + .setCost(new ConfirmationTransaction(TownySettings::getNewNationPrice, capitalTown, "New Nation Cost", + Translatable.of("msg_no_funds_new_nation2", cost))) + .setTitle(Translatable.of("msg_confirm_purchase", prettyMoney(cost))) .sendTo(sender); // Or, it is free, so just make the nation. @@ -1179,6 +1202,13 @@ public void nationAdd(Player player, String[] names) throws TownyException { continue; } + if (nation.hasSanctionedTown(town)) { + // Town is sanctioned and cannot join. + removeinvites.add(townname); + TownyMessaging.sendErrorMsg(player, Translatable.of("msg_err_cannot_add_sanctioned_town", townname)); + continue; + } + if (!testNationMaxResidents(nation, town)) { // Town has too many residents to join the nation removeinvites.add(townname); @@ -1397,6 +1427,73 @@ public static void nationKick(CommandSender sender, Nation nation, List ki TownyMessaging.sendErrorMsg(sender, Translatable.of("msg_invalid_name")); } + public static void nationSanctionTown(CommandSender sender, Nation nation, String[] args) throws TownyException { + if (args.length == 0 || args[0].equals("?")) { + HelpMenu.NATION_SANCTIONTOWN.send(sender); + return; + } + + if (nation == null && sender instanceof Player player) + nation = getNationFromPlayerOrThrow(player); + + if (nation == null) + throw new TownyException(Translatable.of("msg_err_no_nation_cannot_do")); + + if (args[0].toLowerCase(Locale.ROOT).equals("list")) { + if (args.length == 2) + nation = getNationOrThrow(args[1]); + nationSanctionTownList(sender, nation); + return; + } + + if (args.length != 2) { + HelpMenu.NATION_SANCTIONTOWN.send(sender); + return; + } + checkPermOrThrow(sender, PermissionNodes.TOWNY_COMMAND_NATION_SANCTIONTOWN.getNode()); + Town town = getTownOrThrow(args[1]); + switch(args[0].toLowerCase(Locale.ROOT)) { + case "add" -> nationSanctionTownAdd(sender, nation, town); + case "remove" -> nationSactionTownRemove(sender, nation, town); + default -> HelpMenu.NATION_SANCTIONTOWN.send(sender); + } + } + + private static void nationSanctionTownList(CommandSender sender, Nation nation) { + if (nation.getSanctionedTowns().isEmpty()) { + TownyMessaging.sendMsg(sender, Translatable.of("msg_err_nation_has_no_sanctioned_towns")); + return; + } + Translator translator = Translator.locale(sender); + TownyMessaging.sendMessage(sender, ChatTools.formatTitle(nation.getName() + " " + translator.of("title_nation_sanctioned_towns"))); + TownyMessaging.sendMessage(sender, TownyFormatter.getFormattedTownyObjects(translator.of("title_nation_sanctioned_towns"), new ArrayList<>(nation.getSanctionedTowns()))); + } + + private static void nationSanctionTownAdd(CommandSender sender, Nation nation, Town town) throws TownyException { + if (nation.hasTown(town)) + throw new TownyException(Translatable.of("msg_err_nation_cannot_sanction_own_town")); + + if (nation.hasSanctionedTown(town)) + throw new TownyException(Translatable.of("msg_err_nation_town_already_sanctioned")); + + BukkitTools.ifCancelledThenThrow(new NationSanctionTownAddEvent(nation, town)); + + nation.addSanctionedTown(town); + nation.save(); + TownyMessaging.sendMsg(sender, Translatable.of("msg_err_nation_town_sanctioned", town.getName())); + } + + private static void nationSactionTownRemove(CommandSender sender, Nation nation, Town town) throws TownyException { + if (!nation.hasSanctionedTown(town)) + throw new TownyException(Translatable.of("msg_err_nation_town_isnt_sanctioned")); + + BukkitTools.ifCancelledThenThrow(new NationSanctionTownRemoveEvent(nation, town)); + + nation.removeSanctionedTown(town); + nation.save(); + TownyMessaging.sendMsg(sender, Translatable.of("msg_err_nation_town_unsanctioned", town.getName())); + } + private void nationAlly(Player player, String[] split) throws TownyException { if (split.length == 0) { HelpMenu.ALLIES_STRING.send(player); @@ -1973,11 +2070,12 @@ private static void nationSetMapColor(CommandSender sender, Nation nation, Strin if (!TownySettings.getNationColorsMap().containsKey(color)) throw new TownyException(Translatable.of("msg_err_invalid_nation_map_color", TownySettings.getNationColorsMap().keySet().toString())); - if (TownySettings.getNationSetMapColourCost() > 0) + double cost = TownySettings.getNationSetMapColourCost(); + if (cost > 0) Confirmation .runOnAccept(() -> setNationMapColor(nation, color, admin, sender)) - .setTitle(Translatable.of("msg_confirm_purchase", TownySettings.getNationSetMapColourCost())) - .setCost(new ConfirmationTransaction(()-> TownySettings.getNationSetMapColourCost(), nation.getAccount(), "Cost of setting nation map color.")) + .setTitle(Translatable.of("msg_confirm_purchase", prettyMoney(cost))) + .setCost(new ConfirmationTransaction(() -> cost, nation, "Cost of setting nation map color.")) .sendTo(sender); else setNationMapColor(nation, color, admin, sender); diff --git a/Towny/src/main/java/com/palmergames/bukkit/towny/command/PlotCommand.java b/Towny/src/main/java/com/palmergames/bukkit/towny/command/PlotCommand.java index a67ce9d134..c040373a89 100644 --- a/Towny/src/main/java/com/palmergames/bukkit/towny/command/PlotCommand.java +++ b/Towny/src/main/java/com/palmergames/bukkit/towny/command/PlotCommand.java @@ -588,12 +588,12 @@ private void tryPlotSetType(Player player, Resident resident, TownBlock townBloc // Test if we can pay first to throw an exception. if (cost > 0 && TownyEconomyHandler.isActive() && !resident.getAccount().canPayFromHoldings(cost)) - throw new TownyException(Translatable.of("msg_err_cannot_afford_plot_set_type_cost", townBlockType, TownyEconomyHandler.getFormattedBalance(cost))); + throw new TownyException(Translatable.of("msg_err_cannot_afford_plot_set_type_cost", townBlockType, prettyMoney(cost))); // Handle payment via a confirmation to avoid suprise costs. if (cost > 0 && TownyEconomyHandler.isActive()) { Confirmation.runOnAccept(() -> { - TownyMessaging.sendMsg(resident, Translatable.of("msg_plot_set_cost", TownyEconomyHandler.getFormattedBalance(cost), townBlockType)); + TownyMessaging.sendMsg(resident, Translatable.of("msg_plot_set_cost", prettyMoney(cost), townBlockType)); try { townBlock.setType(townBlockType, resident); @@ -604,9 +604,9 @@ private void tryPlotSetType(Player player, Resident resident, TownBlock townBloc BukkitTools.fireEvent(new PlayerChangePlotTypeEvent(townBlockType, oldType, townBlock, player)); TownyMessaging.sendMsg(player, Translatable.of("msg_plot_set_type", townBlockType)); }) - .setCost(new ConfirmationTransaction(() -> cost, resident.getAccount(), String.format("Plot set to %s", townBlockType), - Translatable.of("msg_err_cannot_afford_plot_set_type_cost", townBlockType, TownyEconomyHandler.getFormattedBalance(cost)))) - .setTitle(Translatable.of("msg_confirm_purchase", TownyEconomyHandler.getFormattedBalance(cost))) + .setCost(new ConfirmationTransaction(() -> cost, resident, String.format("Plot set to %s", townBlockType), + Translatable.of("msg_err_cannot_afford_plot_set_type_cost", townBlockType, prettyMoney(cost)))) + .setTitle(Translatable.of("msg_confirm_purchase", prettyMoney(cost))) .sendTo(BukkitTools.getPlayerExact(resident.getName())); // No cost or economy so no confirmation. @@ -648,20 +648,20 @@ public void parsePlotSetOutpost(Player player, Resident resident, TownBlock town // Throws a TownyException with message if outpost should not be set. OutpostUtil.OutpostTests(town, resident, townyWorld, key, resident.isAdmin(), true); - if (TownyEconomyHandler.isActive() && TownySettings.getOutpostCost() > 0) { + if (TownySettings.getOutpostCost() > 0) { // Create a confirmation for setting outpost. Confirmation.runOnAccept(() -> { // Set the outpost spawn and display feedback. town.addOutpostSpawn(player.getLocation()); - TownyMessaging.sendMsg(player, Translatable.of("msg_plot_set_cost", TownyEconomyHandler.getFormattedBalance(TownySettings.getOutpostCost()), Translatable.of("outpost"))); + TownyMessaging.sendMsg(player, Translatable.of("msg_plot_set_cost", prettyMoney(TownySettings.getOutpostCost()), Translatable.of("outpost"))); }) - .setCost(new ConfirmationTransaction(() -> TownySettings.getOutpostCost(), town.getAccount(), "PlotSetOutpost", Translatable.of("msg_err_cannot_afford_to_set_outpost"))) - .setTitle(Translatable.of("msg_confirm_purchase", TownyEconomyHandler.getFormattedBalance(TownySettings.getOutpostCost()))) + .setCost(new ConfirmationTransaction(() -> TownySettings.getOutpostCost(), town, "PlotSetOutpost", Translatable.of("msg_err_cannot_afford_to_set_outpost"))) + .setTitle(Translatable.of("msg_confirm_purchase", prettyMoney(TownySettings.getOutpostCost()))) .sendTo(player); } else { // Set the outpost spawn and display feedback with no cost confirmation. town.addOutpostSpawn(player.getLocation()); - TownyMessaging.sendMsg(player, Translatable.of("msg_plot_set_cost", TownyEconomyHandler.getFormattedBalance(TownySettings.getOutpostCost()), Translatable.of("outpost"))); + TownyMessaging.sendMsg(player, Translatable.of("msg_plot_set_cost", prettyMoney(TownySettings.getOutpostCost()), Translatable.of("outpost"))); } } @@ -994,7 +994,7 @@ public void setPlotForSale(Resident resident, WorldCoord worldCoord, double forS if (forSale != -1) { Translatable message = TownyEconomyHandler.isActive() - ? Translatable.of("msg_plot_for_sale_amount", resident.getName(), worldCoord.toString(), TownyEconomyHandler.getFormattedBalance(townBlock.getPlotPrice())) + ? Translatable.of("msg_plot_for_sale_amount", resident.getName(), worldCoord.toString(), prettyMoney(townBlock.getPlotPrice())) : Translatable.of("msg_plot_for_sale", resident.getName(), worldCoord.toString()); TownyMessaging.sendPrefixedTownMessage(townBlock.getTownOrNull(), message); @@ -1342,7 +1342,7 @@ public void parsePlotGroupForSale(String[] split, Resident resident, TownBlock t group.save(); Translatable message = TownyEconomyHandler.isActive() - ? Translatable.of("msg_player_put_group_up_for_sale_amount", player.getName(), group.getName(), TownyEconomyHandler.getFormattedBalance(group.getPrice())) + ? Translatable.of("msg_player_put_group_up_for_sale_amount", player.getName(), group.getName(), prettyMoney(group.getPrice())) : Translatable.of("msg_player_put_group_up_for_sale", player.getName(), group.getName()); TownyMessaging.sendPrefixedTownMessage(town, message); @@ -1576,7 +1576,7 @@ public void parsePlotGroupSetTownBlockType(String[] split, Resident resident, To double cost = type.getCost() * plotGroupTownBlocks.size(); // Test if we can pay first to throw an exception. if (cost > 0 && TownyEconomyHandler.isActive() && !resident.getAccount().canPayFromHoldings(cost)) - throw new TownyException(Translatable.of("msg_err_cannot_afford_plot_set_type_cost", type, TownyEconomyHandler.getFormattedBalance(cost))); + throw new TownyException(Translatable.of("msg_err_cannot_afford_plot_set_type_cost", type, prettyMoney(cost))); // Handle payment via a confirmation to avoid suprise costs. if (cost > 0 && TownyEconomyHandler.isActive()) { @@ -1584,7 +1584,7 @@ public void parsePlotGroupSetTownBlockType(String[] split, Resident resident, To if (townBlock.getPlotObjectGroup() == null) return; - TownyMessaging.sendMsg(resident, Translatable.of("msg_plot_set_cost", TownyEconomyHandler.getFormattedBalance(cost), type)); + TownyMessaging.sendMsg(resident, Translatable.of("msg_plot_set_cost", prettyMoney(cost), type)); for (TownBlock tb : townBlock.getPlotObjectGroup().getTownBlocks()) { try { @@ -1597,11 +1597,9 @@ public void parsePlotGroupSetTownBlockType(String[] split, Resident resident, To } TownyMessaging.sendMsg(player, Translatable.of("msg_set_group_type_to_x", type)); }) - .setCost(new ConfirmationTransaction(() -> type.getCost() * plotGroupTownBlocks.size(), - resident.getAccount(), - String.format("Plot group (" + plotGroupTownBlocks.size() + ") set to %s", type), - Translatable.of("msg_err_cannot_afford_plot_set_type_cost", type, TownyEconomyHandler.getFormattedBalance(cost)))) - .setTitle(Translatable.of("msg_confirm_purchase", TownyEconomyHandler.getFormattedBalance(cost))) + .setCost(new ConfirmationTransaction(() -> cost, resident, String.format("Plot group (%s) set to %s", plotGroupTownBlocks.size(), type), + Translatable.of("msg_err_cannot_afford_plot_set_type_cost", type, prettyMoney(cost)))) + .setTitle(Translatable.of("msg_confirm_purchase", prettyMoney(cost))) .sendTo(BukkitTools.getPlayerExact(resident.getName())); // No cost or economy so no confirmation. } else { @@ -1837,7 +1835,7 @@ private void continuePlotClaimProcess(List selection, Resident resid PlotGroup group = tb.getPlotObjectGroup(); if (TownyEconomyHandler.isActive() && (!resident.getAccount().canPayFromHoldings(group.getPrice()))) - throw new TownyException(Translatable.of("msg_no_funds_claim_plot_group", group.getTownBlocks().size(), TownyEconomyHandler.getFormattedBalance(group.getPrice()))); + throw new TownyException(Translatable.of("msg_no_funds_claim_plot_group", group.getTownBlocks().size(), prettyMoney(group.getPrice()))); // Add the confirmation for claiming a plot group. Confirmation.runOnAccept(() -> { @@ -1849,7 +1847,7 @@ private void continuePlotClaimProcess(List selection, Resident resid // Execute the plot claim. new PlotClaim(Towny.getPlugin(), player, resident, coords, true, false, true).start(); }) - .setTitle(Translatable.of("msg_plot_group_claim_confirmation", group.getTownBlocks().size()).append(" ").append(TownyEconomyHandler.getFormattedBalance(group.getPrice())).append(". ").append(Translatable.of("are_you_sure_you_want_to_continue"))) + .setTitle(Translatable.of("msg_plot_group_claim_confirmation", group.getTownBlocks().size()).append(" ").append(prettyMoney(group.getPrice())).append(". ").append(Translatable.of("are_you_sure_you_want_to_continue"))) .sendTo(player); return; @@ -1877,7 +1875,7 @@ private void continuePlotClaimProcess(List selection, Resident resid throw new TownyException(Translatable.of("msg_max_plot_own", maxPlots)); if (TownyEconomyHandler.isActive() && (!resident.getAccount().canPayFromHoldings(cost))) - throw new TownyException(Translatable.of("msg_no_funds_claim_plot", TownyEconomyHandler.getFormattedBalance(cost))); + throw new TownyException(Translatable.of("msg_no_funds_claim_plot", prettyMoney(cost))); if (cost != 0) { final List finalSelection = selection; @@ -1885,7 +1883,7 @@ private void continuePlotClaimProcess(List selection, Resident resid // Start the claim task new PlotClaim(plugin, player, resident, finalSelection, true, false, false).start(); }) - .setTitle(Translatable.of("msg_confirm_purchase", TownyEconomyHandler.getFormattedBalance(cost))) + .setTitle(Translatable.of("msg_confirm_purchase", prettyMoney(cost))) .sendTo(player); } else { // Start the claim task diff --git a/Towny/src/main/java/com/palmergames/bukkit/towny/command/ResidentCommand.java b/Towny/src/main/java/com/palmergames/bukkit/towny/command/ResidentCommand.java index 88e191a5ee..ffad445da4 100644 --- a/Towny/src/main/java/com/palmergames/bukkit/towny/command/ResidentCommand.java +++ b/Towny/src/main/java/com/palmergames/bukkit/towny/command/ResidentCommand.java @@ -389,7 +389,7 @@ private void parseResidentJail(Player player, String[] split) throws TownyExcept TownyMessaging.sendErrorMsg(player, Translatable.of("msg_err_unable_to_pay_bail")); } }) - .setTitle(Translatable.of("msg_confirm_purchase", TownyEconomyHandler.getFormattedBalance(cost))) + .setTitle(Translatable.of("msg_confirm_purchase", prettyMoney(cost))) .sendTo(player); } diff --git a/Towny/src/main/java/com/palmergames/bukkit/towny/command/TownCommand.java b/Towny/src/main/java/com/palmergames/bukkit/towny/command/TownCommand.java index 1f1ec4efbb..e92976ff3a 100644 --- a/Towny/src/main/java/com/palmergames/bukkit/towny/command/TownCommand.java +++ b/Towny/src/main/java/com/palmergames/bukkit/towny/command/TownCommand.java @@ -1042,7 +1042,7 @@ private void townPlots(CommandSender sender, String[] args) throws TownyExceptio String plotTypeLine = translator.of("msg_town_plots_type_line", type.getFormattedName(), residentOwned, typeCache.getNumTownBlocks(type, CacheType.FORSALE), typeCache.getNumTownBlocks(type, CacheType.ALL)); if (TownyEconomyHandler.isActive()) - plotTypeLine += translator.of("msg_town_plots_type_line_revenue", TownyEconomyHandler.getFormattedBalance(residentOwned * type.getTax(town))); + plotTypeLine += translator.of("msg_town_plots_type_line_revenue", prettyMoney(residentOwned * type.getTax(town))); out.add(plotTypeLine); } out.add(Translatable.of("msg_town_plots_revenue_disclaimer").forLocale(player)); @@ -1175,7 +1175,7 @@ public void listTowns(CommandSender sender, String[] split) throws TownyExceptio Translatable spawnCost = Translatable.of("msg_spawn_cost_free"); if (TownyEconomyHandler.isActive()) - spawnCost = Translatable.of("msg_spawn_cost", TownyEconomyHandler.getFormattedBalance(town.getSpawnCost())); + spawnCost = Translatable.of("msg_spawn_cost", prettyMoney(town.getSpawnCost())); townName = townName.hoverEvent(HoverEvent.showText(Translatable.of("msg_click_spawn", town).append("\n").append(spawnCost).locale(sender).component())); output.add(Pair.pair(town.getUUID(), townName)); @@ -1419,7 +1419,7 @@ private static void townToggleNeutral(CommandSender sender, boolean admin, Town // If they setting neutral status on send a message confirming they paid something, if they did. if (peacefulState && TownyEconomyHandler.isActive() && cost > 0) { town.getAccount().withdraw(cost, "Peaceful Town Cost"); - TownyMessaging.sendMsg(sender, Translatable.of("msg_you_paid", TownyEconomyHandler.getFormattedBalance(cost))); + TownyMessaging.sendMsg(sender, Translatable.of("msg_you_paid", prettyMoney(cost))); } // Set the toggle setting. @@ -2258,12 +2258,12 @@ public static void townSetName(CommandSender sender, String[] split, Town town) if(TownyEconomyHandler.isActive() && TownySettings.getTownRenameCost() > 0) { if (!town.getAccount().canPayFromHoldings(TownySettings.getTownRenameCost())) - throw new TownyException(Translatable.of("msg_err_no_money", TownyEconomyHandler.getFormattedBalance(TownySettings.getTownRenameCost()))); + throw new TownyException(Translatable.of("msg_err_no_money", prettyMoney(TownySettings.getTownRenameCost()))); final Town finalTown = town; final String finalName = name; Confirmation.runOnAccept(() -> townRename(sender, finalTown, finalName)) - .setTitle(Translatable.of("msg_confirm_purchase", TownyEconomyHandler.getFormattedBalance(TownySettings.getTownRenameCost()))) + .setTitle(Translatable.of("msg_confirm_purchase", prettyMoney(TownySettings.getTownRenameCost()))) .sendTo(sender); } else { townRename(sender, town, name); @@ -2413,8 +2413,8 @@ public static void townSetMapColor(CommandSender sender, String[] split, Town to if (TownySettings.getTownSetMapColourCost() > 0) Confirmation .runOnAccept(()-> setTownMapColor(town, color)) - .setTitle(Translatable.of("msg_confirm_purchase", TownySettings.getTownSetMapColourCost())) - .setCost(new ConfirmationTransaction(()-> TownySettings.getTownSetMapColourCost(), town.getAccount(), "Cost of setting town map color.")) + .setTitle(Translatable.of("msg_confirm_purchase", prettyMoney(TownySettings.getTownSetMapColourCost()))) + .setCost(new ConfirmationTransaction(TownySettings::getTownSetMapColourCost, town, "Cost of setting town map color.")) .sendTo(sender); else setTownMapColor(town, color); @@ -2435,7 +2435,7 @@ public static void townSetTaxPercent(CommandSender sender, String[] split, Town town.setMaxPercentTaxAmount(Double.parseDouble(split[1])); - TownyMessaging.sendPrefixedTownMessage(town, Translatable.of("msg_town_set_tax_max_percent_amount", sender.getName(), TownyEconomyHandler.getFormattedBalance(town.getMaxPercentTaxAmount()))); + TownyMessaging.sendPrefixedTownMessage(town, Translatable.of("msg_town_set_tax_max_percent_amount", sender.getName(), prettyMoney(town.getMaxPercentTaxAmount()))); } private static void parseTownBaltop(Player player, Town town) throws TownyException { @@ -2447,7 +2447,7 @@ private static void parseTownBaltop(Player player, Town town) throws TownyExcept int i = 0; for (Resident res : residents) - sb.append(Translatable.of("msg_baltop_book_format", ++i, res.getName(), TownyEconomyHandler.getFormattedBalance(res.getAccount().getCachedBalance())).forLocale(player) + "\n"); + sb.append(Translatable.of("msg_baltop_book_format", ++i, res.getName(), prettyMoney(res.getAccount().getCachedBalance())).forLocale(player) + "\n"); ItemStack book = BookFactory.makeBook("Town Baltop", town.getName(), sb.toString()); plugin.getScheduler().run(player, () -> player.openBook(book)); @@ -2472,7 +2472,7 @@ else if (TownySettings.isBonusBlocksPerTownLevel() && TownySettings.getMaxBonusB if (split.length == 0 || !split[0].equalsIgnoreCase("bonus")) { TownyMessaging.sendMessage(sender, ChatTools.formatTitle("/town buy")); String line = Colors.Yellow + "[Purchased Bonus] " + Colors.Green + "Cost: " + Colors.LightGreen + "%s" + Colors.Gray + " | " + Colors.Green + "Max: " + Colors.LightGreen + "%d"; - TownyMessaging.sendMessage(sender, String.format(line, TownyEconomyHandler.getFormattedBalance(town.getBonusBlockCost()), TownySettings.getMaxPurchasedBlocks(town))); + TownyMessaging.sendMessage(sender, String.format(line, prettyMoney(town.getBonusBlockCost()), TownySettings.getMaxPurchasedBlocks(town))); if (TownySettings.getPurchasedBonusBlocksIncreaseValue() != 1.0) TownyMessaging.sendMessage(sender, Colors.Green + "Cost Increase per TownBlock: " + Colors.LightGreen + "+" + new DecimalFormat("##.##%").format(TownySettings.getPurchasedBonusBlocksIncreaseValue()-1)); TownyMessaging.sendMessage(sender, ChatTools.formatCommand("", "/town buy", "bonus [n]", "")); @@ -2514,16 +2514,16 @@ public static void townBuyBonusTownBlocks(Town town, int inputN, CommandSender s double cost = town.getBonusBlockCostN(n); // Test if the town can pay and throw economy exception if not. if (!town.getAccount().canPayFromHoldings(cost)) - throw new TownyException(Translatable.of("msg_no_funds_to_buy", n, Translatable.of("bonus_townblocks"), TownyEconomyHandler.getFormattedBalance(cost))); + throw new TownyException(Translatable.of("msg_no_funds_to_buy", n, Translatable.of("bonus_townblocks"), prettyMoney(cost))); Confirmation.runOnAccept(() -> { town.addPurchasedBlocks(n); - TownyMessaging.sendMsg(sender, Translatable.of("msg_buy", n, Translatable.of("bonus_townblocks"), TownyEconomyHandler.getFormattedBalance(cost))); + TownyMessaging.sendMsg(sender, Translatable.of("msg_buy", n, Translatable.of("bonus_townblocks"), prettyMoney(cost))); town.save(); }) - .setCost(new ConfirmationTransaction(() -> cost, town.getAccount(), String.format("Town Buy Bonus (%d)", n), - Translatable.of("msg_no_funds_to_buy", n, Translatable.of("bonus_townblocks"), TownyEconomyHandler.getFormattedBalance(cost)))) - .setTitle(Translatable.of("msg_confirm_purchase", TownyEconomyHandler.getFormattedBalance(cost))) + .setCost(new ConfirmationTransaction(() -> cost, town, String.format("Town Buy Bonus (%d)", n), + Translatable.of("msg_no_funds_to_buy", n, Translatable.of("bonus_townblocks"), prettyMoney(cost)))) + .setTitle(Translatable.of("msg_confirm_purchase", prettyMoney(cost))) .sendTo(sender); } @@ -2587,8 +2587,9 @@ public static void newTown(Player player, String name, Resident resident, boolea } // Test if the resident can afford the town. - if (!resident.getAccount().canPayFromHoldings(TownySettings.getNewTownPrice())) - throw new TownyException(Translatable.of("msg_no_funds_new_town2", (resident.getName().equals(player.getName()) ? Translatable.of("msg_you") : resident.getName()), TownySettings.getNewTownPrice())); + double cost = TownySettings.getNewTownPrice(); + if (!resident.getAccount().canPayFromHoldings(cost)) + throw new TownyException(Translatable.of("msg_no_funds_new_town2", (resident.getName().equals(player.getName()) ? Translatable.of("msg_you") : resident.getName()), cost)); // Send a confirmation before taking their money and throwing the PreNewTownEvent. final String finalName = name; @@ -2603,9 +2604,9 @@ public static void newTown(Player player, String name, Resident resident, boolea } }) .setCancellableEvent(new PreNewTownEvent(player, name, spawnLocation)) - .setTitle(Translatable.of("msg_confirm_purchase", TownyEconomyHandler.getFormattedBalance(TownySettings.getNewTownPrice()))) - .setCost(new ConfirmationTransaction(TownySettings::getNewTownPrice, resident.getAccount(), "New Town Cost", - Translatable.of("msg_no_funds_new_town2", (resident.getName().equals(player.getName()) ? Translatable.of("msg_you") : resident.getName()), TownySettings.getNewTownPrice()))) + .setTitle(Translatable.of("msg_confirm_purchase", prettyMoney(cost))) + .setCost(new ConfirmationTransaction(() -> cost, resident, "New Town Cost", + Translatable.of("msg_no_funds_new_town2", (resident.getName().equals(player.getName()) ? Translatable.of("msg_you") : resident.getName()), prettyMoney(cost)))) .sendTo(player); } @@ -2751,7 +2752,7 @@ public static void townRename(CommandSender sender, Town town, String newName) { double renameCost = TownySettings.getTownRenameCost(); if (TownyEconomyHandler.isActive() && renameCost > 0 && !town.getAccount().withdraw(renameCost, String.format("Town renamed to: %s", newName))) { - TownyMessaging.sendErrorMsg(sender, Translatable.of("msg_err_no_money", TownyEconomyHandler.getFormattedBalance(renameCost))); + TownyMessaging.sendErrorMsg(sender, Translatable.of("msg_err_no_money", prettyMoney(renameCost))); return; } @@ -3637,7 +3638,7 @@ else if (finalSelection.size() == 1) if (!town.getAccount().canPayFromHoldings(blockCost)) { double missingAmount = blockCost - town.getAccount().getHoldingBalance(); - throw new TownyException(Translatable.of("msg_no_funds_claim2", finalSelection.size(), TownyEconomyHandler.getFormattedBalance(blockCost), TownyEconomyHandler.getFormattedBalance(missingAmount), new DecimalFormat("#").format(missingAmount))); + throw new TownyException(Translatable.of("msg_no_funds_claim2", finalSelection.size(), prettyMoney(blockCost), prettyMoney(missingAmount), new DecimalFormat("#").format(missingAmount))); } town.getAccount().withdraw(blockCost, String.format("Town Claim (%d) by %s", finalSelection.size(), player.getName())); @@ -3721,19 +3722,19 @@ public static void parseTownUnclaimCommand(Player player, String[] split) throws if (TownyEconomyHandler.isActive() && TownySettings.getClaimRefundPrice() < 0) { double cost = Math.abs(TownySettings.getClaimRefundPrice() * selection.size()); if (!town.getAccount().canPayFromHoldings(cost)) { - TownyMessaging.sendErrorMsg(player, Translatable.of("msg_err_your_town_cannot_afford_unclaim", TownyEconomyHandler.getFormattedBalance(cost))); + TownyMessaging.sendErrorMsg(player, Translatable.of("msg_err_your_town_cannot_afford_unclaim", prettyMoney(cost))); return; } List finalSelection = selection; Confirmation.runOnAccept(()-> { if (!town.getAccount().canPayFromHoldings(cost)) { - TownyMessaging.sendErrorMsg(player, Translatable.of("msg_err_your_town_cannot_afford_unclaim", TownyEconomyHandler.getFormattedBalance(cost))); + TownyMessaging.sendErrorMsg(player, Translatable.of("msg_err_your_town_cannot_afford_unclaim", prettyMoney(cost))); return; } // Set the area to unclaim plugin.getScheduler().runAsync(new TownClaim(plugin, player, town, finalSelection, false, false, false)); }) - .setTitle(Translatable.of("confirmation_unclaiming_costs", TownyEconomyHandler.getFormattedBalance(cost))) + .setTitle(Translatable.of("confirmation_unclaiming_costs", prettyMoney(cost))) .sendTo(player); return; } @@ -3751,7 +3752,7 @@ private static void parseTownUnclaimAllCommand(Player player, Town town, Residen if (TownyEconomyHandler.isActive() && TownySettings.getClaimRefundPrice() < 0) { int numTownBlocks = town.getTownBlocks().size() - (town.hasHomeBlock() ? 1 : 0); - String formattedCost = TownyEconomyHandler.getFormattedBalance(Math.abs(TownySettings.getClaimRefundPrice() * numTownBlocks)); + String formattedCost = prettyMoney(Math.abs(TownySettings.getClaimRefundPrice() * numTownBlocks)); // Unclaiming will cost the player money because of a negative refund price. Have them confirm the cost. Confirmation .runOnAcceptAsync(new TownClaim(plugin, player, town, null, false, false, false)) @@ -3825,11 +3826,11 @@ private void parseTownTakeoverClaimCommand(Player player) throws TownyException throw new TownyException(Translatable.of("msg_err_another_plugin_cancelled_takeover")); double cost = TownySettings.getTakeoverClaimPrice(); - String costSlug = !TownyEconomyHandler.isActive() || cost <= 0 ? Translatable.of("msg_spawn_cost_free").forLocale(player) : TownyEconomyHandler.getFormattedBalance(cost); + String costSlug = !TownyEconomyHandler.isActive() || cost <= 0 ? Translatable.of("msg_spawn_cost_free").forLocale(player) : prettyMoney(cost); String townName = wc.getTownOrNull().getName(); Confirmation.runOnAccept(() -> Bukkit.getScheduler().runTask(plugin, new TownClaim(plugin, player, town, Arrays.asList(wc), false, true, false))) .setTitle(Translatable.of("confirmation_you_are_about_to_take_over_a_claim", townName, costSlug)) - .setCost(new ConfirmationTransaction(() -> cost, town.getAccount(), "Takeover Claim (" + wc.toString() + ") from " + townName + ".")) + .setCost(new ConfirmationTransaction(() -> cost, town, "Takeover Claim (" + wc.toString() + ") from " + townName + ".")) .sendTo(player); } @@ -3972,10 +3973,6 @@ private static double[] getMergeCosts(Town remainingTown, Town succumbingTown, b return mergeCost; } - private static String prettyMoney(double cost) { - return TownyEconomyHandler.getFormattedBalance(cost); - } - private static void sendTownMergeRequest(CommandSender sender, Town remainingTown, Town succumbingTown, double cost) { TownyMessaging.sendMsg(sender, Translatable.of("msg_town_merge_request_sent", succumbingTown.getName())); TownyMessaging.sendMsg(succumbingTown.getMayor(), Translatable.of("msg_town_merge_request_received", remainingTown.getName(), sender.getName(), remainingTown.getName())); diff --git a/Towny/src/main/java/com/palmergames/bukkit/towny/command/TownyAdminCommand.java b/Towny/src/main/java/com/palmergames/bukkit/towny/command/TownyAdminCommand.java index 5172e148ff..f2614b2077 100644 --- a/Towny/src/main/java/com/palmergames/bukkit/towny/command/TownyAdminCommand.java +++ b/Towny/src/main/java/com/palmergames/bukkit/towny/command/TownyAdminCommand.java @@ -172,6 +172,7 @@ public class TownyAdminCommand extends BaseCommand implements CommandExecutor { "rename", "delete", "toggle", + "sanctiontown", "set", "meta", "deposit", @@ -508,6 +509,9 @@ else if (args.length > 3 && TownyCommandAddonAPI.hasCommand(CommandType.TOWNYADM if (args.length == 2) { return filterByStartOrGetTownyStartingWith(Collections.singletonList("new"), args[1], "+n"); } else if (args.length > 2 && !args[1].equalsIgnoreCase("new")) { + Nation nation = TownyUniverse.getInstance().getNation(args[1]); + if (nation == null) + return Collections.emptyList(); switch (args[2].toLowerCase(Locale.ROOT)) { case "add": if (args.length == 4) @@ -518,14 +522,8 @@ else if (args.length > 3 && TownyCommandAddonAPI.hasCommand(CommandType.TOWNYADM else if (args.length == 5) return NameUtil.filterByStart(BaseCommand.setOnOffCompletes, args[4]); case "set": { - Nation nation = TownyUniverse.getInstance().getNation(args[1]); - if (nation != null) { - if (args.length == 4) { - return NameUtil.filterByStart(adminNationSetTabCompletes, args[3]); - } - } - else { - return Collections.emptyList(); + if (args.length == 4) { + return NameUtil.filterByStart(adminNationSetTabCompletes, args[3]); } } case "meta": @@ -546,6 +544,16 @@ else if (args.length == 5) return getTownyStartingWith(args[4], "r"); else if (args.length == 6) return NameUtil.filterByStart(TownyPerms.getNationRanks(), args[5]); + case "sanctiontown": + if (args.length == 4) + return NameUtil.filterByStart(Arrays.asList("add","remove","list"), args[3]); + if (args.length == 5 && args[3].equalsIgnoreCase("add") || args[4].equalsIgnoreCase("remove")) + return NameUtil.filterByStart(TownyUniverse.getInstance().getTowns() + .stream() + .filter(t -> !nation.hasTown(t)) + .map(Town::getName) + .collect(Collectors.toList()), args[4]); + break; case "enemy": case "ally": if (args.length == 4) @@ -1633,6 +1641,10 @@ public void parseAdminNationCommand(CommandSender sender, String[] split) throws checkPermOrThrow(sender, PermissionNodes.TOWNY_COMMAND_TOWNYADMIN_NATION_KICK.getNode()); NationCommand.nationKick(sender, nation, TownyAPI.getInstance().getTowns(StringMgmt.remArgs(split, 2))); break; + case "sanctiontown": + checkPermOrThrow(sender, PermissionNodes.TOWNY_COMMAND_TOWNYADMIN_NATION_SANCTIONTOWN.getNode()); + NationCommand.nationSanctionTown(sender, nation, StringMgmt.remArgs(split, 2)); + break; case "delete": checkPermOrThrow(sender, PermissionNodes.TOWNY_COMMAND_TOWNYADMIN_NATION_DELETE.getNode()); Confirmation.runOnAccept(() -> { diff --git a/Towny/src/main/java/com/palmergames/bukkit/towny/command/TownyCommand.java b/Towny/src/main/java/com/palmergames/bukkit/towny/command/TownyCommand.java index fe6bd9fff1..75d7f6932f 100644 --- a/Towny/src/main/java/com/palmergames/bukkit/towny/command/TownyCommand.java +++ b/Towny/src/main/java/com/palmergames/bukkit/towny/command/TownyCommand.java @@ -412,10 +412,10 @@ public List getTownyPrices(Town town, Translator translator) { nation = town.getNationOrNull(); output.add(ChatTools.formatTitle(translator.of("towny_prices_title"))); - output.add(translator.of("towny_prices_town_nation", getMoney(TownySettings.getNewTownPrice()), getMoney(TownySettings.getNewNationPrice()))); - output.add(translator.of("towny_prices_reclaim", getMoney(TownySettings.getEcoPriceReclaimTown()))); + output.add(translator.of("towny_prices_town_nation", prettyMoney(TownySettings.getNewTownPrice()), prettyMoney(TownySettings.getNewNationPrice()))); + output.add(translator.of("towny_prices_reclaim", prettyMoney(TownySettings.getEcoPriceReclaimTown()))); if (town != null) { - output.add(translator.of("towny_prices_upkeep", getMoney(TownySettings.getTownUpkeepCost(town)), getMoney(TownySettings.getNationUpkeepCost(nation)))); + output.add(translator.of("towny_prices_upkeep", prettyMoney(TownySettings.getTownUpkeepCost(town)), prettyMoney(TownySettings.getNationUpkeepCost(nation)))); output.add(translator.of("towny_prices_upkeep_based_on", (TownySettings.isUpkeepByPlot() ? translator.of("towny_prices_upkeep_num_plots") : translator.of("towny_prices_upkeep_town_level")))); String upkeepformula; if (TownySettings.isNationUpkeepPerPlot()) @@ -426,36 +426,36 @@ else if (TownySettings.isNationUpkeepPerTown()) upkeepformula = translator.of("towny_prices_upkeep_nation_level"); output.add(translator.of("towny_prices_nation_upkeep_based_on", upkeepformula)); if (town.isOverClaimed() && TownySettings.getUpkeepPenalty() > 0) - output.add(translator.of("towny_prices_overclaimed_upkeep", getMoney(TownySettings.getTownPenaltyUpkeepCost(town)))); + output.add(translator.of("towny_prices_overclaimed_upkeep", prettyMoney(TownySettings.getTownPenaltyUpkeepCost(town)))); if (TownySettings.getUpkeepPenalty() > 0 ) output.add(translator.of("towny_prices_overclaimed_based_on", (TownySettings.isUpkeepPenaltyByPlot() ? translator.of("towny_prices_overclaimed_num_plots") : translator.of("towny_prices_overclaimed_flat_cost")), TownySettings.getUpkeepPenalty())); - output.add(translator.of("towny_prices_town_merge", getMoney(TownySettings.getBaseCostForTownMerge()), getMoney(town.getTownBlockCost()/2))); - output.add(translator.of("towny_prices_claiming_townblock", getMoney(town.getTownBlockCost()) + + output.add(translator.of("towny_prices_town_merge", prettyMoney(TownySettings.getBaseCostForTownMerge()), prettyMoney(town.getTownBlockCost()/2))); + output.add(translator.of("towny_prices_claiming_townblock", prettyMoney(town.getTownBlockCost()) + (Double.valueOf(TownySettings.getClaimPriceIncreaseValue()).equals(1.0) ? "" : translator.of("towny_prices_claiming_townblock_increase", new DecimalFormat("##.##%").format(TownySettings.getClaimPriceIncreaseValue()-1))))); - output.add(translator.of("towny_prices_claiming_outposts", getMoney(TownySettings.getOutpostCost()))); + output.add(translator.of("towny_prices_claiming_outposts", prettyMoney(TownySettings.getOutpostCost()))); } if (town == null) - output.add(translator.of("towny_prices_upkeep", getMoney(TownySettings.getTownUpkeep()), getMoney(TownySettings.getNationUpkeep()))); + output.add(translator.of("towny_prices_upkeep", prettyMoney(TownySettings.getTownUpkeep()), prettyMoney(TownySettings.getNationUpkeep()))); if (town != null) { output.add(translator.of("towny_prices_townname", town.getFormattedName())); - output.add(translator.of("towny_prices_price_plot", getMoney(town.getPlotPrice()),getMoney(TownySettings.getOutpostCost()))); - output.add(translator.of("towny_prices_price_shop", getMoney(town.getCommercialPlotPrice()), getMoney(town.getEmbassyPlotPrice()))); + output.add(translator.of("towny_prices_price_plot", prettyMoney(town.getPlotPrice()),prettyMoney(TownySettings.getOutpostCost()))); + output.add(translator.of("towny_prices_price_shop", prettyMoney(town.getCommercialPlotPrice()), prettyMoney(town.getEmbassyPlotPrice()))); - output.add(translator.of("towny_prices_taxes_plot", (town.isTaxPercentage()? town.getTaxes() + "%" : getMoney(town.getTaxes())), getMoney(town.getPlotTax()))); - output.add(translator.of("towny_prices_taxes_shop", getMoney(town.getCommercialPlotTax()), getMoney(town.getEmbassyPlotTax()))); - output.add(translator.of("towny_prices_town_neutral_tax", getMoney(TownySettings.getTownNeutralityCost(town)))); + output.add(translator.of("towny_prices_taxes_plot", (town.isTaxPercentage()? town.getTaxes() + "%" : prettyMoney(town.getTaxes())), prettyMoney(town.getPlotTax()))); + output.add(translator.of("towny_prices_taxes_shop", prettyMoney(town.getCommercialPlotTax()), prettyMoney(town.getEmbassyPlotTax()))); + output.add(translator.of("towny_prices_town_neutral_tax", prettyMoney(TownySettings.getTownNeutralityCost(town)))); output.add(translator.of("towny_prices_plots")); List townBlockTypes = new ArrayList<>(TownBlockTypeHandler.getTypes().values()); for (int i = 0; i < townBlockTypes.size(); i++) { if (i == townBlockTypes.size() - 1) - output.add(translator.of("towny_prices_type_single", townBlockTypes.get(i).getFormattedName(), getMoney(townBlockTypes.get(i).getCost()))); + output.add(translator.of("towny_prices_type_single", townBlockTypes.get(i).getFormattedName(), prettyMoney(townBlockTypes.get(i).getCost()))); else { output.add(translator.of("towny_prices_type_double", - townBlockTypes.get(i).getFormattedName(), getMoney(townBlockTypes.get(i).getCost()), - townBlockTypes.get(i+1).getFormattedName(), getMoney(townBlockTypes.get(i+1).getCost()) + townBlockTypes.get(i).getFormattedName(), prettyMoney(townBlockTypes.get(i).getCost()), + townBlockTypes.get(i+1).getFormattedName(), prettyMoney(townBlockTypes.get(i+1).getCost()) )); i++; @@ -464,16 +464,12 @@ else if (TownySettings.isNationUpkeepPerTown()) if (nation != null) { output.add(translator.of("towny_prices_nationname", nation.getFormattedName())); - output.add(translator.of("towny_prices_nation_tax", (nation.isTaxPercentage() ? nation.getTaxes() + "%" : getMoney(nation.getTaxes())), getMoney(TownySettings.getNationNeutralityCost(nation)))); + output.add(translator.of("towny_prices_nation_tax", (nation.isTaxPercentage() ? nation.getTaxes() + "%" : prettyMoney(nation.getTaxes())), prettyMoney(TownySettings.getNationNeutralityCost(nation)))); } } return output; } - - private String getMoney(double cost) { - return TownyEconomyHandler.getFormattedBalance(cost); - } - + public List getTopBankBalance(final List governments) { final int maxListing = TownySettings.getTownyTopSize(); final List output = new ArrayList<>(); @@ -488,7 +484,7 @@ public List getTopBankBalance(final List governments) { if (maxListing != -1 && index > maxListing) { break; } - output.add(String.format(Colors.LightGray + "%-20s " + Colors.Gold + "|" + Colors.Blue + " %s", gov.getFormattedName(), getMoney(gov.getAccount().getCachedBalance()))); + output.add(String.format(Colors.LightGray + "%-20s " + Colors.Gold + "|" + Colors.Blue + " %s", gov.getFormattedName(), prettyMoney(gov.getAccount().getCachedBalance()))); } return output; } diff --git a/Towny/src/main/java/com/palmergames/bukkit/towny/confirmations/ConfirmationHandler.java b/Towny/src/main/java/com/palmergames/bukkit/towny/confirmations/ConfirmationHandler.java index f93c5b4c85..25ddd466c1 100644 --- a/Towny/src/main/java/com/palmergames/bukkit/towny/confirmations/ConfirmationHandler.java +++ b/Towny/src/main/java/com/palmergames/bukkit/towny/confirmations/ConfirmationHandler.java @@ -141,9 +141,9 @@ public static void acceptConfirmation(CommandSender sender) { // Determine the cost, done in this phase in case the cost could be manipulated before confirming. transaction.supplyCost(); double cost = transaction.getCost(); + Account payee = transaction.getPayee(); // Can they pay the cost? - if (cost > 0) { - Account payee = transaction.getPayee(); + if (cost > 0 && payee != null) { if (!payee.canPayFromHoldings(cost)) { TownyMessaging.sendErrorMsg(sender, transaction.getInsufficientFundsMessage()); TownyMessaging.sendErrorMsg(sender, Translatable.of("msg_err_you_need_x_to_pay", cost)); diff --git a/Towny/src/main/java/com/palmergames/bukkit/towny/confirmations/ConfirmationTransaction.java b/Towny/src/main/java/com/palmergames/bukkit/towny/confirmations/ConfirmationTransaction.java index 490f9b8f43..da0b6927ee 100644 --- a/Towny/src/main/java/com/palmergames/bukkit/towny/confirmations/ConfirmationTransaction.java +++ b/Towny/src/main/java/com/palmergames/bukkit/towny/confirmations/ConfirmationTransaction.java @@ -2,7 +2,10 @@ import java.util.function.Supplier; +import org.jetbrains.annotations.Nullable; + import com.palmergames.bukkit.towny.TownyEconomyHandler; +import com.palmergames.bukkit.towny.object.EconomyHandler; import com.palmergames.bukkit.towny.object.Translatable; import com.palmergames.bukkit.towny.object.economy.Account; @@ -17,15 +20,15 @@ public class ConfirmationTransaction { * A transaction which must succeed for a Confirmation to complete. * * @param costSupplier cost of the transaction. - * @param payee Account which will have to pay. + * @param townyObject EconomyHandler which will have to pay, this will be a Resident, Town or Nation. * @param loggedMessage The message logged in the money.csv file. * @param insufficientFundsMessage Transatable which will display the cannot pay message. */ - public ConfirmationTransaction(Supplier costSupplier, Account payee, String loggedMessage, Translatable insufficientFundsMessage) { + public ConfirmationTransaction(Supplier costSupplier, EconomyHandler townyObject, String loggedMessage, Translatable insufficientFundsMessage) { this.costSupplier = costSupplier; - this.payee = payee; this.loggedMessage = loggedMessage; this.insufficientFundsMessage = insufficientFundsMessage; + this.payee = !TownyEconomyHandler.isActive() ? null : townyObject.getAccount(); } /** @@ -33,14 +36,14 @@ public ConfirmationTransaction(Supplier costSupplier, Account payee, Str * Uses the default no money error message. * * @param costSupplier cost of the transaction. - * @param payee Account which will have to pay. + * @param townyObject EconomyHandler which will have to pay, this will be a Resident, Town or Nation. * @param loggedMessage The message logged in the money.csv file. */ - public ConfirmationTransaction(Supplier costSupplier, Account payee, String loggedMessage) { + public ConfirmationTransaction(Supplier costSupplier, EconomyHandler townyObject, String loggedMessage) { this.costSupplier = costSupplier; - this.payee = payee; this.loggedMessage = loggedMessage; this.insufficientFundsMessage = null; + this.payee = !TownyEconomyHandler.isActive() ? null : townyObject.getAccount(); } public void supplyCost() { @@ -51,6 +54,7 @@ public double getCost() { return cost; } + @Nullable public Account getPayee() { return payee; } @@ -62,4 +66,38 @@ public String getLoggedMessage() { public Translatable getInsufficientFundsMessage() { return insufficientFundsMessage != null ? insufficientFundsMessage : Translatable.of("msg_err_no_money", TownyEconomyHandler.getFormattedBalance(getCost())); } + + /** + * A transaction which must succeed for a Confirmation to complete. + * + * @deprecated since 0.100.0.10 use {@link #ConfirmationTransaction(Supplier, EconomyHandler, String, Translatable)} instead. + * @param costSupplier cost of the transaction. + * @param payee Account which will have to pay. + * @param loggedMessage The message logged in the money.csv file. + * @param insufficientFundsMessage Transatable which will display the cannot pay message. + */ + @Deprecated + public ConfirmationTransaction(Supplier costSupplier, Account payee, String loggedMessage, Translatable insufficientFundsMessage) { + this.costSupplier = costSupplier; + this.payee = payee; + this.loggedMessage = loggedMessage; + this.insufficientFundsMessage = insufficientFundsMessage; + } + + /** + * A transaction which must succeed for a Confirmation to complete. + * Uses the default no money error message. + * + * @deprecated since 0.100.0.10 use {@link #ConfirmationTransaction(Supplier, EconomyHandler, String)} instead. + * @param costSupplier cost of the transaction. + * @param payee Account which will have to pay. + * @param loggedMessage The message logged in the money.csv file. + */ + @Deprecated + public ConfirmationTransaction(Supplier costSupplier, Account payee, String loggedMessage) { + this.costSupplier = costSupplier; + this.payee = payee; + this.loggedMessage = loggedMessage; + this.insufficientFundsMessage = null; + } } diff --git a/Towny/src/main/java/com/palmergames/bukkit/towny/db/SQLSchema.java b/Towny/src/main/java/com/palmergames/bukkit/towny/db/SQLSchema.java index db381c9024..7f59bfadd2 100644 --- a/Towny/src/main/java/com/palmergames/bukkit/towny/db/SQLSchema.java +++ b/Towny/src/main/java/com/palmergames/bukkit/towny/db/SQLSchema.java @@ -269,6 +269,7 @@ private static List getNationColumns(){ columns.add("`isOpen` bool NOT NULL DEFAULT '1'"); columns.add("`metadata` text DEFAULT NULL"); columns.add("`conqueredTax` float NOT NULL"); + columns.add("`sanctionedTowns` mediumtext DEFAULT NULL"); return columns; } diff --git a/Towny/src/main/java/com/palmergames/bukkit/towny/db/TownyFlatFileSource.java b/Towny/src/main/java/com/palmergames/bukkit/towny/db/TownyFlatFileSource.java index 7c95e759c7..db38f3f0a5 100644 --- a/Towny/src/main/java/com/palmergames/bukkit/towny/db/TownyFlatFileSource.java +++ b/Towny/src/main/java/com/palmergames/bukkit/towny/db/TownyFlatFileSource.java @@ -1218,6 +1218,11 @@ public boolean loadNation(Nation nation) { if (line != null && !line.isEmpty()) nation.setConqueredTax(Double.parseDouble(line)); + line = keys.get("sanctionedTowns"); + if (line != null) { + nation.loadSanctionedTowns(line.split("#")); + } + } catch (Exception e) { plugin.getLogger().log(Level.WARNING, Translation.of("flatfile_err_reading_nation_file_at_line", nation.getName(), line, nation.getName()), e); return false; @@ -2136,6 +2141,9 @@ public boolean saveNation(Nation nation) { list.add("metadata=" + serializeMetadata(nation)); list.add("conqueredTax=" + nation.getConqueredTax()); + + // SanctionedTowns + list.add("sanctionedTowns=" + StringMgmt.join(nation.getSanctionedTownsForSaving(), "#")); /* * Make sure we only save in async */ diff --git a/Towny/src/main/java/com/palmergames/bukkit/towny/db/TownySQLSource.java b/Towny/src/main/java/com/palmergames/bukkit/towny/db/TownySQLSource.java index 5c54d58866..9c3e0cd008 100644 --- a/Towny/src/main/java/com/palmergames/bukkit/towny/db/TownySQLSource.java +++ b/Towny/src/main/java/com/palmergames/bukkit/towny/db/TownySQLSource.java @@ -1387,6 +1387,11 @@ private boolean loadNation(ResultSet rs) { } catch (SQLException ignored) { } + line = rs.getString("sanctionedTowns"); + if (line != null) { + nation.loadSanctionedTowns(line.split("#")); + } + return true; } catch (SQLException e) { TownyMessaging.sendErrorMsg("SQL: Load Nation " + name + " SQL Error - " + e.getMessage()); @@ -2349,7 +2354,7 @@ public synchronized boolean saveNation(Nation nation) { nat_hm.put("metadata", ""); nat_hm.put("conqueredTax", nation.getConqueredTax()); - + nat_hm.put("sanctionedTowns", StringMgmt.join(nation.getSanctionedTownsForSaving(), "#")); updateDB("NATIONS", nat_hm, Collections.singletonList("name")); } catch (Exception e) { diff --git a/Towny/src/main/java/com/palmergames/bukkit/towny/event/nation/NationSanctionTownAddEvent.java b/Towny/src/main/java/com/palmergames/bukkit/towny/event/nation/NationSanctionTownAddEvent.java new file mode 100644 index 0000000000..34f31113b0 --- /dev/null +++ b/Towny/src/main/java/com/palmergames/bukkit/towny/event/nation/NationSanctionTownAddEvent.java @@ -0,0 +1,38 @@ +package com.palmergames.bukkit.towny.event.nation; + +import com.palmergames.bukkit.towny.event.CancellableTownyEvent; +import com.palmergames.bukkit.towny.object.Nation; +import com.palmergames.bukkit.towny.object.Town; + +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; + +public class NationSanctionTownAddEvent extends CancellableTownyEvent { + private static final HandlerList HANDLER_LIST = new HandlerList(); + + private final Nation nation; + private final Town town; + + public NationSanctionTownAddEvent(Nation nation, Town town) { + this.nation = nation; + this.town = town; + } + + public Nation getNation() { + return nation; + } + + public Town getTown() { + return town; + } + + public static HandlerList getHandlerList() { + return HANDLER_LIST; + } + + @NotNull + @Override + public HandlerList getHandlers() { + return HANDLER_LIST; + } +} diff --git a/Towny/src/main/java/com/palmergames/bukkit/towny/event/nation/NationSanctionTownRemoveEvent.java b/Towny/src/main/java/com/palmergames/bukkit/towny/event/nation/NationSanctionTownRemoveEvent.java new file mode 100644 index 0000000000..75517e988d --- /dev/null +++ b/Towny/src/main/java/com/palmergames/bukkit/towny/event/nation/NationSanctionTownRemoveEvent.java @@ -0,0 +1,38 @@ +package com.palmergames.bukkit.towny.event.nation; + +import com.palmergames.bukkit.towny.event.CancellableTownyEvent; +import com.palmergames.bukkit.towny.object.Nation; +import com.palmergames.bukkit.towny.object.Town; + +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; + +public class NationSanctionTownRemoveEvent extends CancellableTownyEvent { + private static final HandlerList HANDLER_LIST = new HandlerList(); + + private final Nation nation; + private final Town town; + + public NationSanctionTownRemoveEvent(Nation nation, Town town) { + this.nation = nation; + this.town = town; + } + + public Nation getNation() { + return nation; + } + + public Town getTown() { + return town; + } + + public static HandlerList getHandlerList() { + return HANDLER_LIST; + } + + @NotNull + @Override + public HandlerList getHandlers() { + return HANDLER_LIST; + } +} diff --git a/Towny/src/main/java/com/palmergames/bukkit/towny/object/Nation.java b/Towny/src/main/java/com/palmergames/bukkit/towny/object/Nation.java index c57c5718e7..cdaf705950 100644 --- a/Towny/src/main/java/com/palmergames/bukkit/towny/object/Nation.java +++ b/Towny/src/main/java/com/palmergames/bukkit/towny/object/Nation.java @@ -34,6 +34,7 @@ import java.util.Collections; import java.util.List; import java.util.Objects; +import java.util.UUID; import java.util.stream.Collectors; public class Nation extends Government { @@ -41,6 +42,7 @@ public class Nation extends Government { private static final String ECONOMY_ACCOUNT_PREFIX = TownySettings.getNationAccountPrefix(); private final List towns = new ArrayList<>(); + private final List sanctionedTowns = new ArrayList<>(); private List allies = new ArrayList<>(); private List enemies = new ArrayList<>(); private Town capital; @@ -698,4 +700,38 @@ public int getLevel(int populationSize) { public void playerBroadCastMessageToNation(Player player, String message) { TownyMessaging.sendPrefixedNationMessage(this, Translatable.of("town_say_format", player.getName(), TownyComponents.stripClickTags(message))); } + + + public List getSanctionedTowns() { + return sanctionedTowns; + } + + public boolean hasSanctionedTown(Town town) { + return sanctionedTowns.contains(town); + } + + public void addSanctionedTown(Town town) { + if (!sanctionedTowns.contains(town)) + sanctionedTowns.add(town); + } + + public void removeSanctionedTown(Town town) { + sanctionedTowns.remove(town); + } + + public List getSanctionedTownsForSaving() { + return sanctionedTowns.stream().map(t -> t.getUUID().toString()).collect(Collectors.toList()); + } + + public void loadSanctionedTowns(String[] tokens) { + for (String stringUUID : tokens) { + try { + Town town = TownyAPI.getInstance().getTown(UUID.fromString(stringUUID)); + if (town != null) + sanctionedTowns.add(town); + } catch (IllegalArgumentException ignored) { + continue; + } + } + } } diff --git a/Towny/src/main/java/com/palmergames/bukkit/towny/permissions/PermissionNodes.java b/Towny/src/main/java/com/palmergames/bukkit/towny/permissions/PermissionNodes.java index f81f85110e..6c5a59dafe 100644 --- a/Towny/src/main/java/com/palmergames/bukkit/towny/permissions/PermissionNodes.java +++ b/Towny/src/main/java/com/palmergames/bukkit/towny/permissions/PermissionNodes.java @@ -90,6 +90,7 @@ public enum PermissionNodes { TOWNY_COMMAND_NATION_BANKHISTORY("towny.command.nation.bankhistory"), TOWNY_COMMAND_NATION_BALTOP("towny.command.nation.baltop"), TOWNY_COMMAND_NATION_KICK("towny.command.nation.kick"), + TOWNY_COMMAND_NATION_SANCTIONTOWN("towny.command.nation.sanctiontown"), /* * Town command permissions @@ -338,6 +339,7 @@ public enum PermissionNodes { TOWNY_COMMAND_TOWNYADMIN_NATION_RECHECK("towny.command.townyadmin.nation.recheck"), TOWNY_COMMAND_TOWNYADMIN_NATION_RENAME("towny.command.townyadmin.nation.rename"), TOWNY_COMMAND_TOWNYADMIN_NATION_MERGE("towny.command.townyadmin.nation.merge"), + TOWNY_COMMAND_TOWNYADMIN_NATION_SANCTIONTOWN("towny.command.townyadmin.nation.sanctiontown"), TOWNY_COMMAND_TOWNYADMIN_NATION_SET("towny.command.townyadmin.nation.set"), TOWNY_COMMAND_TOWNYADMIN_NATION_SETFOUNDINGDATE("towny.command.townyadmin.nation.set.foundingdate"), TOWNY_COMMAND_TOWNYADMIN_NATION_TOGGLE("towny.command.townyadmin.nation.toggle"), diff --git a/Towny/src/main/java/com/palmergames/bukkit/towny/utils/TownRuinUtil.java b/Towny/src/main/java/com/palmergames/bukkit/towny/utils/TownRuinUtil.java index e2fb73ebf1..506d7a9a19 100644 --- a/Towny/src/main/java/com/palmergames/bukkit/towny/utils/TownRuinUtil.java +++ b/Towny/src/main/java/com/palmergames/bukkit/towny/utils/TownRuinUtil.java @@ -153,14 +153,11 @@ public static void processRuinedTownReclaimRequest(Player player) { if (TownySettings.getTownRuinsMinDurationHours() - getTimeSinceRuining(town) > 0) throw new TownyException(Translatable.of("msg_err_cannot_reclaim_town_yet", TownySettings.getTownRuinsMinDurationHours() - getTimeSinceRuining(town))); - if (TownyEconomyHandler.isActive() && townReclaimCost > 0) { - Confirmation.runOnAccept(() -> reclaimTown(resident, town)) - .setCost(new ConfirmationTransaction(() -> townReclaimCost, resident.getAccount(), "Cost of town reclaim.", Translatable.of("msg_insuf_funds"))) - .setTitle(Translatable.of("msg_confirm_purchase", TownyEconomyHandler.getFormattedBalance(townReclaimCost))) - .sendTo(player); - } else { - reclaimTown(resident, town); - } + //Ask them to confirm they want to reclaim the town. + Confirmation.runOnAccept(() -> reclaimTown(resident, town)) + .setCost(new ConfirmationTransaction(() -> townReclaimCost, resident, "Cost of town reclaim.", Translatable.of("msg_insuf_funds"))) + .setTitle(Translatable.of("msg_confirm_purchase", TownyEconomyHandler.getFormattedBalance(townReclaimCost))) + .sendTo(player); } catch (TownyException e) { TownyMessaging.sendErrorMsg(player, e.getMessage(player)); } diff --git a/Towny/src/main/java/com/palmergames/bukkit/util/NameValidation.java b/Towny/src/main/java/com/palmergames/bukkit/util/NameValidation.java index 6bcc8d0395..8e90a68361 100644 --- a/Towny/src/main/java/com/palmergames/bukkit/util/NameValidation.java +++ b/Towny/src/main/java/com/palmergames/bukkit/util/NameValidation.java @@ -29,7 +29,7 @@ public class NameValidation { "outlawlist","deposit","outlaw","outpost","ranklist","rank","reclaim","reslist","say","set","toggle","join", "invite","buy","mayor","bankhistory","enemy","ally","townlist","allylist","enemylist","king","merge","jail", "plotgrouplist","trust","purge","leader","baltop","all","help", "spawn", "takeoverclaim", "ban", "unjail", - "trusttown","forsale","fs","notforsale","nfs","buytown")); + "trusttown","forsale","fs","notforsale","nfs","buytown","sanctiontown")); } /** diff --git a/Towny/src/main/resources/ChangeLog.txt b/Towny/src/main/resources/ChangeLog.txt index fc35a08749..b708dc797c 100644 --- a/Towny/src/main/resources/ChangeLog.txt +++ b/Towny/src/main/resources/ChangeLog.txt @@ -9285,4 +9285,34 @@ v0.92.0.11: - This setting is only used when nation_proximity_to_capital_city is above 0. - Leave this setting at 0.0 in order to allow nations to chain towns together to go as wide as they like. 0.100.0.11: - - Fix config migrator file formatting, courtesy of Warrior with PR #7128. \ No newline at end of file + - Fix config migrator file formatting, courtesy of Warrior with PR #7128. +0.100.0.12: + - Bump adventure-platform to 4.3.2 for 1.20.4 compatibility. +0.100.0.13: + - Bump org.apache.maven.plugins:maven-surefire-plugin from 3.1.2 to 3.2.3. + - Bump com.github.seeseemelk:MockBukkit-v1.20 from 3.56.0 to 3.58.0. + - Make Towny more resiliant to errors which come from having no Economy present on the server. + - Add the ability for nations to sanction towns, preventing them from joining the nation via invite or join. + - Using /n sanctiontown ... kings can add/remove and list their sanctioned towns. + - Closes #6544. + - New Command: /nation sanctiontown + - add [townname] - Adds a town to the sanctioned town list. + - remove [townname] - Removes a town from the sanctioned town list. + - list - Lists your nation's sanctioned towns. + - list [nationname] - Lists the sanctioned towns of other nations. + - New Command: /ta nation [nationname] sanctiontown + - add [townname] - Adds a town to the sanctioned town list. + - remove [townname] - Removes a town from the sanctioned town list. + - list - Lists the nation's sanctioned towns. + - New Permission Node: towny.command.nation.sanctiontown + - Allows adding/removing sanctioned towns, a child node of towny.command.nation.*. + - No townyperms.yml change is required. + - New Permission Node: towny.command.townyadmin.nation.sanctiontown + - Allows admins to add/remove sanctioned towns from a given nation, a child node of towny.command.townyadmin.nation.*. + - No townyperms.yml change is required. + - API: Added NationSanctionTownAddEvent + - A cancellable event which is fired before a town becomes sanctioned. + - API: Added NationSanctionTownRemoveEvent + - A cancellable event which is fired before a town becomes unsanctioned. +0.100.0.14: + - Fix potential IllegalArgumentException by loading sanctioned towns in a safer way. \ No newline at end of file diff --git a/Towny/src/main/resources/lang/de-DE.yml b/Towny/src/main/resources/lang/de-DE.yml index 3ded6ba674..4a6895cb67 100644 --- a/Towny/src/main/resources/lang/de-DE.yml +++ b/Towny/src/main/resources/lang/de-DE.yml @@ -103,13 +103,13 @@ town_list_help_5: "Listet Städte nach beanspruchtem Land mit der angegebenen Se town_list_help_6: "Listet die Städte, mit den meisten Bewohnern online mit der angegebenen Seite auf." town_set_help_0: "Set your town's board." town_set_help_1: "Set your town's homeblock to where you're standing." -town_set_help_2: "Sets the spawn point in your homeblock or an outpost spawnpoint when stood in an outpost." +town_set_help_2: "Setzt deinen Stadt spawn Punkt, oder Outpost spawn punkt, solange du dich in einem Outpost befindest." town_set_help_3: "See /town set perm ? for help." -town_set_help_4: "Set your town's tax amount." +town_set_help_4: "Setze deine Stadt Steuer fest." town_set_help_5: "Set the special amounts for per-plot taxes." -town_set_help_6: "Set the default prices for buying plots." +town_set_help_6: "Setzte die Grundgebühren für Plots fest." town_set_help_7: "Set the cost to use /t spawn." -town_set_help_8: "Set your town's name." +town_set_help_8: "Lege deinen Stadt-Namen fest." town_set_help_9: "Set your town's tag." town_set_help_10: "Set the title or surname of your resident." town_set_help_11: "Set the max amount of money a percentage tax can take from a resident." diff --git a/Towny/src/main/resources/lang/en-US.yml b/Towny/src/main/resources/lang/en-US.yml index ebb3a4c368..c9b567b105 100644 --- a/Towny/src/main/resources/lang/en-US.yml +++ b/Towny/src/main/resources/lang/en-US.yml @@ -206,6 +206,11 @@ nation_toggle_help_1: "Toggles public status, allowing non-residents to use /n s nation_toggle_help_2: "Toggles open status, allowing towns to join without an invite." nation_toggle_help_3: "Toggles taxpercent, making towns pay a percentage instead of fixed rate." +nation_sanction_help_1: "Adds a town to the SanctionedTown list." +nation_sanction_help_2: "Removes a town from the SanctionedTown list." +nation_sanction_help_3: "Lists your nation's Sanctioned Towns." +nation_sanction_help_4: "Lists the given nation's Sanctioned Towns." + king_help_1: 'Nation Leader Help' king_help_2: 'Set your alliance.' king_help_3: 'Set your enemies.' @@ -2371,4 +2376,24 @@ msg_err_resident_is_npc: "You cannot do this to an NPC resident." # Message shown when a resident doesn't own any land and the /res plotlist command is used. msg_err_resident_doesnt_own_any_land: "The resident does not own any land personally." -msg_unnamed: "Unnamed" \ No newline at end of file +msg_unnamed: "Unnamed" + +msg_err_nation_cannot_sanction_own_town: "You cannot sanction a town that is a part of your nation." + +msg_err_nation_town_isnt_sanctioned: "Your nation is not sanctioning that town." + +msg_err_nation_town_already_sanctioned: "Your nation already sanctions that town." + +msg_err_nation_town_sanctioned: "Your nation has now sanctioned %s, they will not be allowed to join your nation." + +msg_err_nation_town_unsanctioned: "Your nation is no longer sanctioning %s." + +msg_err_nation_has_no_sanctioned_towns: "The nation has no sanctioned towns." + +title_nation_sanctioned_towns: 'Sanctioned Towns' + +msg_err_cannot_add_sanctioned_town: "Your nation cannot add %s, your nation has sanctioned them." + +msg_err_cannot_join_nation_sanctioned_town: "Your town cannot join %s, this nation has sanctioned your town." + +msg_err_no_nation_cannot_do: "You cannot perform this action because no nation was able to be determined." \ No newline at end of file diff --git a/Towny/src/main/resources/plugin.yml b/Towny/src/main/resources/plugin.yml index 640db5bc98..4986906125 100644 --- a/Towny/src/main/resources/plugin.yml +++ b/Towny/src/main/resources/plugin.yml @@ -211,6 +211,7 @@ permissions: towny.command.nation.othernation: true towny.command.nation.withdraw: true towny.command.nation.rank.*: true + towny.command.nation.sanctiontown: true towny.command.nation.say: true towny.command.nation.set.*: true towny.command.nation.spawn: true @@ -746,6 +747,7 @@ permissions: towny.command.townyadmin.nation.rank: true towny.command.townyadmin.nation.recheck: true towny.command.townyadmin.nation.rename: true + towny.command.townyadmin.nation.sanctiontown: true towny.command.townyadmin.nation.set: true towny.command.townyadmin.nation.set.*: true towny.command.townyadmin.nation.toggle: true