diff --git a/pom.xml b/pom.xml index 7a579b0..372de0f 100644 --- a/pom.xml +++ b/pom.xml @@ -55,6 +55,15 @@ spigotmc-repo https://hub.spigotmc.org/nexus/content/repositories/snapshots/ + + essentialsx-releases + EssentialsX API Repository + https://repo.essentialsx.net/releases + + + paper-repo + https://repo.papermc.io/repository/maven-public/ + @@ -75,5 +84,11 @@ 26.0.2 compile + + net.essentialsx + EssentialsX + 2.21.2 + provided + diff --git a/src/main/java/fr/molzonas/painfulloss/PainfulLoss.java b/src/main/java/fr/molzonas/painfulloss/PainfulLoss.java index f609eee..5c9cc2f 100644 --- a/src/main/java/fr/molzonas/painfulloss/PainfulLoss.java +++ b/src/main/java/fr/molzonas/painfulloss/PainfulLoss.java @@ -3,18 +3,25 @@ package fr.molzonas.painfulloss; import fr.molzonas.painfulloss.commands.PainfulLossCommand; import fr.molzonas.painfulloss.enums.PropertiesEnum; import fr.molzonas.painfulloss.listeners.DeathListener; +import fr.molzonas.painfulloss.provider.CountPriceProvider; +import fr.molzonas.painfulloss.provider.EssentialsPriceProvider; +import fr.molzonas.painfulloss.provider.PriceProvider; import fr.molzonas.painfulloss.utils.Message; import lombok.Getter; import org.bukkit.Bukkit; import org.bukkit.command.PluginCommand; +import org.bukkit.plugin.Plugin; import org.bukkit.plugin.java.JavaPlugin; import java.util.Locale; +import java.util.Optional; +import java.util.logging.Level; public final class PainfulLoss extends JavaPlugin { public static final Locale DEFAULT_LOCALE = Locale.ENGLISH; public static final String DEFAULT_LOCALE_TAG = "en-US"; @Getter private static PainfulLoss instance = null; + @Getter private static PriceProvider priceProvider = null; @Override public void onEnable() { @@ -23,13 +30,14 @@ public final class PainfulLoss extends JavaPlugin { configurationInit(); langInit(); registerCommands(); + initPriceProvider(); registerListeners(); - Bukkit.getLogger().info("[PainfulLoss] Plugin has been enabled!"); + info("Plugin has been enabled!"); } @Override public void onDisable() { - Bukkit.getLogger().info("[PainfulLoss] Plugin has been disabled!"); + info("Plugin has been disabled!"); } private void configurationInit() { @@ -56,9 +64,54 @@ public final class PainfulLoss extends JavaPlugin { Bukkit.getPluginManager().registerEvents(new DeathListener(), this); } + private void initPriceProvider() { + Optional ultimateShopProvider = Optional.empty(); + Optional essentialsProvider = Optional.empty(); + PriceProvider countProvider = new CountPriceProvider(); + + // Ultimate Shop + Plugin ultimateShop = getServer().getPluginManager().getPlugin("UltimateShop"); + if (ultimateShop != null && ultimateShop.isEnabled()) { + info("UltimateShop detected (but inactive for Painful Loss)"); + } + + // Essentials + Plugin essentials = getServer().getPluginManager().getPlugin("Essentials"); + if (essentials instanceof com.earth2me.essentials.IEssentials ess && essentials.isEnabled()) { + EssentialsPriceProvider epp = new EssentialsPriceProvider(ess); + if (epp.isReady()) { + essentialsProvider = Optional.of(epp); + info("Essentials detected, worth.yml loaded"); + } else { + info("Essentials detected, worth.yml empty or unavailable"); + } + } + + // Selection + String provider = PropertiesEnum.PRICE_PROVIDER.getValue(); + if (provider != null) { + switch (provider.toLowerCase()) { + case "ultimateshop": priceProvider = ultimateShopProvider.orElse(countProvider); break; + case "essentials": priceProvider = essentialsProvider.orElse(countProvider); break; + default: priceProvider = countProvider; break; + } + } else { + priceProvider = ultimateShopProvider.orElse(essentialsProvider.orElse(countProvider)); + } + } + public void reload() { reloadConfig(); Message.clearCache(); langInit(); + initPriceProvider(); + } + + public static void log(Level level, String message) { + Bukkit.getLogger().log(level, "[Painful Loss] {0}", message); + } + + public static void info(String message) { + PainfulLoss.log(Level.INFO, message); } } diff --git a/src/main/java/fr/molzonas/painfulloss/commands/PainfulLossCommand.java b/src/main/java/fr/molzonas/painfulloss/commands/PainfulLossCommand.java index 5f679d9..4e5ea6f 100644 --- a/src/main/java/fr/molzonas/painfulloss/commands/PainfulLossCommand.java +++ b/src/main/java/fr/molzonas/painfulloss/commands/PainfulLossCommand.java @@ -2,6 +2,7 @@ package fr.molzonas.painfulloss.commands; import fr.molzonas.painfulloss.PainfulLoss; import fr.molzonas.painfulloss.utils.Message; +import fr.molzonas.painfulloss.utils.PriceCalculator; import org.bukkit.ChatColor; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; @@ -17,7 +18,8 @@ import java.util.Locale; public class PainfulLossCommand implements CommandExecutor, TabCompleter { @Override - public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, @NotNull String[] args) { + public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, + @NotNull String s, @NotNull String[] args) { if (args.length == 0) { commandSender.sendMessage(Message.of("plugin.help")); return true; @@ -27,6 +29,7 @@ public class PainfulLossCommand implements CommandExecutor, TabCompleter { case "reload" -> commandReload(commandSender); case "help" -> commandHelp(commandSender); case "test" -> commandTest(commandSender, args); + case "worth" -> commandWorth(commandSender, args); default -> commandUnknown(commandSender); }; } @@ -39,12 +42,14 @@ public class PainfulLossCommand implements CommandExecutor, TabCompleter { } private boolean commandNotAutorised(CommandSender commandSender) { - commandSender.sendMessage(ChatColor.translateAlternateColorCodes('&', "&4You don't have the permission to execute this command !")); + commandSender.sendMessage(ChatColor.translateAlternateColorCodes('&', + "&4You don't have the permission to execute this command !")); return true; } private boolean commandHelp(CommandSender commandSender) { - commandSender.sendMessage(ChatColor.translateAlternateColorCodes('&', "&4G&3i&et &ag&co&9o&dd")); + commandSender.sendMessage(ChatColor.translateAlternateColorCodes('&', + "&4G&3i&et &ag&co&9o&dd")); return true; } @@ -54,7 +59,7 @@ public class PainfulLossCommand implements CommandExecutor, TabCompleter { locale = Locale.forLanguageTag(args[1].trim()); } if (commandSender instanceof Player p) { - p.sendMessage(Message.of("death.summary", locale, p.getName(), 12345)); + p.sendMessage(Message.of("death.summary.count", locale, p.getName(), 12345)); p.sendMessage(Message.of("death.topitem", locale, "Netherite Chestplate", 7890)); } else { var text = Message.of("death.summary.first", locale, "Console", 42); @@ -68,9 +73,26 @@ public class PainfulLossCommand implements CommandExecutor, TabCompleter { return false; } + private boolean commandWorth(CommandSender commandSender, String[] args) { + if (args.length == 1 && commandSender instanceof Player p && commandSender.hasPermission("painfulloss.worth")) { + commandSender.sendMessage(Message.of("command.worth", PriceCalculator.estimate(p))); + } else if (args.length == 2 && commandSender.hasPermission("painfulloss.worth.other")) { + Player p = PainfulLoss.getInstance().getServer().getPlayer(args[1]); + if (p == null) { + commandSender.sendMessage(Message.of("command.worth.unknownplayer", args[1])); + return true; + } + commandSender.sendMessage(Message.of("command.worth.other", PriceCalculator.estimate(p))); + } else { + return commandNotAutorised(commandSender); + } + return false; + } @Override - public @Nullable List onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, @NotNull String[] args) { + public @Nullable List onTabComplete(@NotNull CommandSender commandSender, + @NotNull Command command, @NotNull String s, + @NotNull String[] args) { List completions = new ArrayList<>(); if (args.length == 1) { completions.add("test"); diff --git a/src/main/java/fr/molzonas/painfulloss/enums/PropertiesEnum.java b/src/main/java/fr/molzonas/painfulloss/enums/PropertiesEnum.java index 68f2258..95a1211 100644 --- a/src/main/java/fr/molzonas/painfulloss/enums/PropertiesEnum.java +++ b/src/main/java/fr/molzonas/painfulloss/enums/PropertiesEnum.java @@ -5,11 +5,17 @@ import lombok.Getter; public enum PropertiesEnum { LOCALE("locale", PainfulLoss.DEFAULT_LOCALE_TAG), - DEBUG("debug", "false"); + DEBUG("debug", "false"), + PRICE_PROVIDER("priceProvider") + ; @Getter private final String path; @Getter private String fallback = null; + PropertiesEnum(String path) { + this.path = path; + } + PropertiesEnum(String path, String fallback) { this.path = path; this.fallback = fallback; diff --git a/src/main/java/fr/molzonas/painfulloss/listeners/DeathListener.java b/src/main/java/fr/molzonas/painfulloss/listeners/DeathListener.java index d7435f2..8c7ad0b 100644 --- a/src/main/java/fr/molzonas/painfulloss/listeners/DeathListener.java +++ b/src/main/java/fr/molzonas/painfulloss/listeners/DeathListener.java @@ -1,6 +1,8 @@ package fr.molzonas.painfulloss.listeners; +import fr.molzonas.painfulloss.PainfulLoss; import fr.molzonas.painfulloss.utils.Message; +import fr.molzonas.painfulloss.utils.PriceCalculator; import org.bukkit.GameRule; import org.bukkit.Material; import org.bukkit.entity.Player; @@ -9,6 +11,7 @@ import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.entity.PlayerDeathEvent; import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; import java.util.ArrayList; import java.util.List; @@ -27,8 +30,23 @@ public class DeathListener implements Listener { if (lostItems.isEmpty()) return; - int cnt = lostItems.stream().mapToInt(ItemStack::getAmount).sum(); + double total = PriceCalculator.estimate(lostItems); - player.sendMessage(Message.of("death.summary", player.getName(), cnt)); + if (PainfulLoss.getPriceProvider().isCountBased()) { + player.sendMessage(Message.of("death.summary.count", player.getName(), total)); + } else { + player.sendMessage(Message.of("death.summary.value", player.getName(), numberFormat(total))); + } + } + + protected static String nameFormat(ItemStack s) { + ItemMeta meta = s.getItemMeta(); + if (meta != null && meta.hasDisplayName()) return meta.getDisplayName(); + return s.getType().name(); + } + + protected static String numberFormat(double v) { + if (v == (long) v) return String.format("%d", (long) v); + return String.format(Message.getCurrentLocale(), "%.2f", v); } } diff --git a/src/main/java/fr/molzonas/painfulloss/provider/CountPriceProvider.java b/src/main/java/fr/molzonas/painfulloss/provider/CountPriceProvider.java new file mode 100644 index 0000000..4ee8b60 --- /dev/null +++ b/src/main/java/fr/molzonas/painfulloss/provider/CountPriceProvider.java @@ -0,0 +1,22 @@ +package fr.molzonas.painfulloss.provider; + +import org.bukkit.inventory.ItemStack; + +import java.util.Optional; + +public class CountPriceProvider implements PriceProvider{ + @Override + public String getName() { + return "Count"; + } + + @Override + public Optional getPrice(ItemStack stack) { + return Optional.of((double) stack.getAmount()); + } + + @Override + public boolean isCountBased() { + return true; + } +} diff --git a/src/main/java/fr/molzonas/painfulloss/provider/EssentialsPriceProvider.java b/src/main/java/fr/molzonas/painfulloss/provider/EssentialsPriceProvider.java new file mode 100644 index 0000000..0c3decf --- /dev/null +++ b/src/main/java/fr/molzonas/painfulloss/provider/EssentialsPriceProvider.java @@ -0,0 +1,36 @@ +package fr.molzonas.painfulloss.provider; + +import com.earth2me.essentials.Worth; +import com.earth2me.essentials.IEssentials; +import org.bukkit.inventory.ItemStack; + +import java.math.BigDecimal; +import java.util.Optional; + +public class EssentialsPriceProvider implements PriceProvider { + private final IEssentials plugin; + private final Worth worth; + public EssentialsPriceProvider(IEssentials plugin) { + this.plugin = plugin; + this.worth = plugin.getWorth(); + } + + @Override + public String getName() { + return "Essentials"; + } + + @Override + public Optional getPrice(ItemStack stack) { + if (stack == null || stack.getAmount() <= 0) return Optional.empty(); + + try { + BigDecimal price = worth.getPrice(plugin, stack); + if (price == null) return Optional.empty(); + if (price.signum() < 0) return Optional.empty(); + return Optional.of(price.doubleValue()); + } catch (Exception e) { + return Optional.empty(); + } + } +} diff --git a/src/main/java/fr/molzonas/painfulloss/provider/PriceProvider.java b/src/main/java/fr/molzonas/painfulloss/provider/PriceProvider.java new file mode 100644 index 0000000..86e2179 --- /dev/null +++ b/src/main/java/fr/molzonas/painfulloss/provider/PriceProvider.java @@ -0,0 +1,12 @@ +package fr.molzonas.painfulloss.provider; + +import org.bukkit.inventory.ItemStack; + +import java.util.Optional; + +public interface PriceProvider { + String getName(); + Optional getPrice(ItemStack stack); + default boolean isReady() { return true; } + default boolean isCountBased() { return false; } +} diff --git a/src/main/java/fr/molzonas/painfulloss/utils/Message.java b/src/main/java/fr/molzonas/painfulloss/utils/Message.java index 48d5632..2a08768 100644 --- a/src/main/java/fr/molzonas/painfulloss/utils/Message.java +++ b/src/main/java/fr/molzonas/painfulloss/utils/Message.java @@ -1,6 +1,7 @@ package fr.molzonas.painfulloss.utils; import fr.molzonas.painfulloss.PainfulLoss; +import lombok.Getter; import org.bukkit.ChatColor; import org.jetbrains.annotations.NotNull; @@ -14,7 +15,7 @@ public class Message { private static final String DIRECTORY = "lang"; private static final String BUNDLE_NAME = "messages"; private static final ConcurrentHashMap CACHE = new ConcurrentHashMap<>(); - private static Locale currentLocale = Locale.ENGLISH; + @Getter private static Locale currentLocale = Locale.ENGLISH; private Message() {} diff --git a/src/main/java/fr/molzonas/painfulloss/utils/PriceCalculator.java b/src/main/java/fr/molzonas/painfulloss/utils/PriceCalculator.java new file mode 100644 index 0000000..74deeac --- /dev/null +++ b/src/main/java/fr/molzonas/painfulloss/utils/PriceCalculator.java @@ -0,0 +1,31 @@ +package fr.molzonas.painfulloss.utils; + +import fr.molzonas.painfulloss.PainfulLoss; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; + +import java.util.List; + +public class PriceCalculator { + private PriceCalculator() {} + + public static Double estimate(Player player) { + return estimate(player.getInventory().getContents()); + } + + public static Double estimate(ItemStack[] items) { + double rs = 0d; + for (ItemStack item : items) { + rs += estimate(item); + } + return rs; + } + + public static Double estimate(List items) { + return items.stream().mapToDouble(PriceCalculator::estimate).sum(); + } + + public static Double estimate(ItemStack item) { + return PainfulLoss.getPriceProvider().getPrice(item).orElse(0d); + } +} diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 530e0fd..80afabd 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,5 +1,27 @@ -# Available : en-US, fr-FR +# Available : en-US, fr-FR, and more if you add your own files locale: en-US # Dev option, you won't have to enable it, trust me -debug: false \ No newline at end of file +debug: false + +# Select the price provider between Essentials (worth.yml), UltimateShop (value) and Count (number of items). +# Keep it empty will take the first available in order between UltimateShop, Essentials and Count. +priceProvider: + +# How estimations works, +estimation: + xp: + enabled: true + price_per_point: 0.5 + show_in_message: true + show_in_message_with_level: true + + enchant: + enabled: true + default_per_level: 0 + table: + SHARPNESS: 100 + EFFICIENCY: 100 + UNBREAKING: 50 + FORTUNE: 300 + MENDING: 1000 \ No newline at end of file diff --git a/src/main/resources/lang/messages_en.properties b/src/main/resources/lang/messages_en.properties index f4cd121..56de8f4 100644 --- a/src/main/resources/lang/messages_en.properties +++ b/src/main/resources/lang/messages_en.properties @@ -1,4 +1,8 @@ -death.summary=&c{0} lost &e{1}&c. +death.summary.value=&c{0} lost the equivalent of &e{1}$&c. +death.summary.count=&c{0} lost &e{1}&c item(s). death.topitem=&7Most valuable: &6{0} &7(&e{1}&7) +command.worth=&6Your full inventory is worth {0}$. +command.worth.other=&6{0} full inventory is worth {1}$. +command.worth.unknownplayer=&cThe requested player ({0}) has not been found. plugin.reload=&aPainfulLoss has successfully reloaded. plugin.unknown=&4Unknown command. Please use &c/painfullost help &4to get some help. \ No newline at end of file diff --git a/src/main/resources/lang/messages_fr.properties b/src/main/resources/lang/messages_fr.properties index 763e44c..5b08dbc 100644 --- a/src/main/resources/lang/messages_fr.properties +++ b/src/main/resources/lang/messages_fr.properties @@ -1,4 +1,8 @@ -death.summary=&c{0} a perdu &e{1}&c. +death.summary.value=&c{0} a perdu l'équivalent de &e{1}$&c. +death.summary.count=&c{0} a perdu &e{1}&c item(s). death.topitem=&7Objet le plus cher : &6{0} &7(&e{1}&7) +command.worth=&6Votre inventaire complet vaut {0}$. +command.worth.other=&6L''inventaire complet de {0} vaut {1}$. +command.worth.unknownplayer=&cLe joueur demandé ({0}) n''a pas été trouvé. plugin.reload=&aPainfulLoss a été rechargé avec succès. plugin.unknown=&4Commande inconnue. Merci d'utiliser &c/painfulloss help &4pour obtenir de l'aide. \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index fe68653..7293f7d 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -2,12 +2,19 @@ name: PainfulLoss version: '1.0' main: fr.molzonas.painfulloss.PainfulLoss api-version: '1.20' +softdepend: [Essentials, UltimateShop] author: Molzonas commands: painfulloss: description: Base command for Painfulloss - usage: /painfulloss + usage: /painfulloss permissions: painfulloss.admin: description: Access to PainfulLoss admin commands + default: op + painfulloss.worth: + description: Worth your own inventory + default: true + painfulloss.worth.other: + description: Worth other players inventory default: op \ No newline at end of file