From 0786e47359b2980b401077e8f1e49c64e5021e3e Mon Sep 17 00:00:00 2001 From: contactvertexdev Date: Fri, 23 Oct 2026 12:39:52 +0200 Subject: [PATCH 1/2] =?UTF-8?q?feat(rarity):=20limites=20par=20=C3=A9quipe?= =?UTF-8?q?ment,=20annonces=20de=20craft=20et=20symboles?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Améliore le système de rareté avec de nouvelles options dans rarity.yml : - symbol : symbole affiché devant le nom de la rareté (et du perk si symbol_in_name est activé) - max_per_item : nombre maximum de perks d'une rareté par équipement, avec un message configurable (limit_message) - craft_announce et craft_sound : message diffusé au serveur et son joué au joueur qui craft un perk de cette rareté Le RarityManager vérifie aussi au démarrage que les ids cités dans rarity.yml correspondent à des perks existants et avertit dans la console sinon. La limite est branchée dans SmithingListener et l'annonce dans CraftingListener. --- .../smp/core/listeners/CraftingListener.java | 1 + .../smp/core/listeners/SmithingListener.java | 1 + .../smp/core/manager/rarity/Rarity.java | 13 ++- .../core/manager/rarity/RarityManager.java | 96 ++++++++++++++++++- src/main/resources/rarity.yml | 52 +++++++++- 5 files changed, 152 insertions(+), 11 deletions(-) diff --git a/src/main/java/fr/antidino/smp/core/listeners/CraftingListener.java b/src/main/java/fr/antidino/smp/core/listeners/CraftingListener.java index ef765d0..a36dd22 100644 --- a/src/main/java/fr/antidino/smp/core/listeners/CraftingListener.java +++ b/src/main/java/fr/antidino/smp/core/listeners/CraftingListener.java @@ -59,6 +59,7 @@ public void onCraftingEvent(CraftItemEvent event) { } cooldownManager.setCooldown(playerId,0.3); Bukkit.getScheduler().runTaskAsynchronously(plugin,()->{ + plugin.rarityManager.onCraft(player, perk, plugin.pdcBuilder.getPerkLevel(resultType)); plugin.databaseProvider.InsertPerkCraftSQL(id,playerId.toString()); }); } diff --git a/src/main/java/fr/antidino/smp/core/listeners/SmithingListener.java b/src/main/java/fr/antidino/smp/core/listeners/SmithingListener.java index 6e64648..f621f1b 100644 --- a/src/main/java/fr/antidino/smp/core/listeners/SmithingListener.java +++ b/src/main/java/fr/antidino/smp/core/listeners/SmithingListener.java @@ -38,6 +38,7 @@ public void onPrepareSmithing(PrepareSmithingEvent event) { String perk_id = pdc.get(PDCBuilder.PERK_ID, PersistentDataType.STRING); AbstractPerk perk = plugin.perkManager.getPerk(perk_id); if (perk == null) return; + if (!plugin.rarityManager.canApply((org.bukkit.entity.Player) event.getView().getPlayer(), equip, perk)) return; int level = Math.min(plugin.pdcBuilder.getPerkLevel(mineral), perk.getMaxLevel()); apply_perk(event,equip,perk_id,level); diff --git a/src/main/java/fr/antidino/smp/core/manager/rarity/Rarity.java b/src/main/java/fr/antidino/smp/core/manager/rarity/Rarity.java index 187651e..cc5b462 100644 --- a/src/main/java/fr/antidino/smp/core/manager/rarity/Rarity.java +++ b/src/main/java/fr/antidino/smp/core/manager/rarity/Rarity.java @@ -3,15 +3,20 @@ import net.kyori.adventure.text.Component; import net.kyori.adventure.text.minimessage.MiniMessage; -public record Rarity(String id, String displayName, String color, boolean colorName, int order) { +public record Rarity(String id, String displayName, String color, String symbol, boolean colorName, + int maxPerItem, String announce, String sound, int order) { private static final MiniMessage MM = MiniMessage.miniMessage(); + public String labelMini() { + return color + (symbol.isEmpty() ? "" : symbol + " ") + displayName; + } + public Component label() { - return MM.deserialize("" + color + displayName); + return MM.deserialize("" + labelMini()); } - public String labelMini() { - return color + displayName; + public boolean hasLimit() { + return maxPerItem >= 0; } } diff --git a/src/main/java/fr/antidino/smp/core/manager/rarity/RarityManager.java b/src/main/java/fr/antidino/smp/core/manager/rarity/RarityManager.java index e98cc6a..d462cbf 100644 --- a/src/main/java/fr/antidino/smp/core/manager/rarity/RarityManager.java +++ b/src/main/java/fr/antidino/smp/core/manager/rarity/RarityManager.java @@ -2,21 +2,24 @@ import fr.antidino.smp.CorePlugin; import fr.antidino.smp.core.manager.perk.AbstractPerk; +import fr.antidino.smp.core.manager.perk.PerkEntry; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.minimessage.MiniMessage; +import org.bukkit.Bukkit; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; import java.io.File; +import java.util.Collection; +import java.util.Collections; import java.util.LinkedHashMap; +import java.util.List; import java.util.Locale; import java.util.Map; -/** - * Charge rarity.yml : des niveaux de rareté ( - * auxquels on associe des perks par id. - */ public class RarityManager { private static final MiniMessage MM = MiniMessage.miniMessage(); @@ -27,8 +30,10 @@ public class RarityManager { private final Map perkRarity = new LinkedHashMap<>(); private Rarity defaultRarity; private boolean colorPerkName = true; + private boolean symbolInName = false; private boolean sortCodex = true; private String rarityLine = "Rareté : %rarity%"; + private String limitMessage = "Cet équipement a déjà %max% perk(s) %rarity% !"; public RarityManager(CorePlugin plugin) { this.plugin = plugin; @@ -45,8 +50,11 @@ public void reload() { defaultRarity = null; colorPerkName = config.getBoolean("color_perk_name", true); + symbolInName = config.getBoolean("symbol_in_name", false); sortCodex = config.getBoolean("sort_codex_by_rarity", true); rarityLine = config.getString("rarity_line", "Rareté : %rarity%"); + limitMessage = config.getString("limit_message", + "Cet équipement a déjà %max% perk(s) %rarity% !"); ConfigurationSection sec = config.getConfigurationSection("rarities"); if (sec != null) { @@ -58,7 +66,11 @@ public void reload() { id, r.getString("display_name", id), r.getString("color", ""), + r.getString("symbol", ""), r.getBoolean("color_name", colorPerkName), + r.getInt("max_per_item", -1), + r.getString("craft_announce", ""), + r.getString("craft_sound", ""), order++); rarities.put(id.toLowerCase(Locale.ROOT), rarity); @@ -82,6 +94,22 @@ public void reload() { plugin.getLogger().warning("[rarity] default_rarity '" + def + "' n'existe pas."); } } + + // Vérification des ids une fois tous les perks chargés (au prochain tick) + Bukkit.getScheduler().runTask(plugin, this::validate); + } + + /** Avertit dans la console si rarity.yml cite un perk qui n'existe pas. */ + private void validate() { + if (plugin.perkManager == null) return; + for (String key : perkRarity.keySet()) { + boolean found = plugin.perkManager.getPerks().stream() + .anyMatch(p -> normalize(p.getId()).equals(key)); + if (!found) { + plugin.getLogger().warning("[rarity] perk inconnu dans rarity.yml : '" + key + + "' (voir les ids avec /perk give)"); + } + } } private static String normalize(String id) { @@ -89,6 +117,13 @@ private static String normalize(String id) { return s.endsWith("_perk") ? s.substring(0, s.length() - 5) : s; } + // ------------------------------------------------------------ LECTURE + + public Collection getRarities() { + return Collections.unmodifiableCollection(rarities.values()); + } + + /** Rareté d'une perk (celle par défaut s'il n'est listé nulle part, null si aucune). */ public Rarity getRarity(String perkId) { Rarity r = perkRarity.get(normalize(perkId)); return r != null ? r : defaultRarity; @@ -98,15 +133,18 @@ public Rarity getRarity(AbstractPerk perk) { return perk == null ? null : getRarity(perk.getId()); } + /** Nom du perk (sans italique), coloré selon sa rareté si activé dans rarity.yml. */ public Component styledName(AbstractPerk perk) { Rarity rarity = getRarity(perk); if (rarity == null || !rarity.colorName()) { return MM.deserialize("" + perk.getDisplayName()); } + String symbol = symbolInName && !rarity.symbol().isEmpty() ? rarity.symbol() + " " : ""; String plain = MM.stripTags(perk.getDisplayName()); - return MM.deserialize("" + rarity.color() + plain); + return MM.deserialize("" + rarity.color() + symbol + plain); } + /** Ligne "Rareté : X" pour le codex, ou null si le perk n'a pas de rareté. */ public Component rarityLine(AbstractPerk perk) { Rarity rarity = getRarity(perk); if (rarity == null) return null; @@ -117,8 +155,56 @@ public boolean sortCodexByRarity() { return sortCodex; } + /** Ordre de tri : plus la rareté est haute, plus le nombre est grand (sans rareté = -1). */ public int order(AbstractPerk perk) { Rarity r = getRarity(perk); return r == null ? -1 : r.order(); } + + // ------------------------------------------------------------ LIMITES + + /** + * Vérifie la limite "max_per_item" de la rareté du perk sur cet équipement. + * Envoie le message "limit_message" au joueur (si non null) quand c'est refusé. + */ + public boolean canApply(Player viewer, ItemStack equipment, AbstractPerk perk) { + Rarity rarity = getRarity(perk); + if (rarity == null || !rarity.hasLimit()) return true; + + int count = 0; + List applied = plugin.pdcBuilder.getAppliedPerks(equipment); + for (String raw : applied) { + AbstractPerk other = plugin.perkManager.getPerk(PerkEntry.parse(raw).id()); + if (other != null && getRarity(other) == rarity) count++; + } + if (count < rarity.maxPerItem()) return true; + + if (viewer != null) { + viewer.sendMessage(MM.deserialize(limitMessage + .replace("%max%", String.valueOf(rarity.maxPerItem())) + .replace("%rarity%", rarity.labelMini()))); + } + return false; + } + + // ------------------------------------------------------------ CRAFT + + /** Annonce + son quand un joueur craft un perk dont la rareté en définit un. */ + public void onCraft(Player player, AbstractPerk perk, int level) { + Rarity rarity = getRarity(perk); + if (rarity == null) return; + + if (!rarity.announce().isBlank()) { + String plain = MM.stripTags(perk.getDisplayName()); + String text = rarity.announce() + .replace("%player%", player.getName()) + .replace("%perk%", rarity.color() + plain) + .replace("%level%", AbstractPerk.roman(Math.max(1, level))) + .replace("%rarity%", rarity.labelMini()); + Bukkit.broadcast(MM.deserialize(text)); + } + if (!rarity.sound().isBlank()) { + player.playSound(player.getLocation(), rarity.sound(), 1.0f, 1.0f); + } + } } diff --git a/src/main/resources/rarity.yml b/src/main/resources/rarity.yml index 02f0c69..848a4eb 100644 --- a/src/main/resources/rarity.yml +++ b/src/main/resources/rarity.yml @@ -1,24 +1,63 @@ +# ============================================================================ +# RARETÉS DES PERKS (plugins/SMPplugin/rarity.yml) +# Tout le texte et toutes les couleurs sont en MiniMessage uniquement. +# Rechargement à chaud : /perk reload +# ============================================================================ +# +# Exemples de couleurs valides pour "color" : +# "" couleur simple +# "<#ff8800>" couleur hexadécimale +# "" dégradé 2 couleurs +# "" dégradé multi-couleurs +# ============================================================================ + +# Rareté donnée aux perks qui ne sont listés dans aucune rareté. +# Mettre "" (vide) pour ne rien afficher pour ces perks. default_rarity: "common" +# true = le nom du perk prend la couleur / le dégradé de sa rareté +# (dans le lore des équipements et dans le codex) +# false = le nom garde la couleur définie dans son propre fichier color_perk_name: true -# Ligne affichée dans le codex +# true = le symbole de la rareté est aussi affiché devant le nom du perk +symbol_in_name: false + +# Ligne affichée dans le codex (liste + détail). %rarity% = symbole + nom coloré. rarity_line: "Rareté : %rarity%" +# true = le codex trie les perks de la plus commune à la plus rare (puis par id) sort_codex_by_rarity: true +# Message envoyé quand la limite "max_per_item" d'une rareté est atteinte. +# Variables : %max% (limite), %rarity% (nom coloré de la rareté) +limit_message: "Cet équipement a déjà %max% perk(s) %rarity% !" + # Les raretés sont classées dans l'ordre du fichier : la première est la plus commune. +# +# Options d'une rareté : +# display_name nom affiché +# color couleur / dégradé (voir plus haut) +# symbol (optionnel) symbole devant le nom, ex: "✦" +# color_name (optionnel) surcharge "color_perk_name" pour cette rareté +# max_per_item (optionnel) nombre max de perks de cette rareté par équipement, -1 = illimité +# craft_announce (optionnel) message diffusé à tout le serveur quand un perk est crafté +# variables : %player% %perk% %level% %rarity% +# craft_sound (optionnel) son joué au joueur qui craft, ex: "entity.player.levelup" +# perks ids des perks (ceux de /perk give). Le suffixe "_perk" est facultatif. rarities: common: display_name: "Commun" color: "" - perks: # ids des perks + symbol: "•" + perks: - "sprinter_perk" - "night_vision_perk" uncommon: display_name: "Peu commun" color: "" + symbol: "◆" perks: - "health_perk" - "speed_perk" @@ -29,6 +68,7 @@ rarities: rare: display_name: "Rare" color: "" + symbol: "✦" perks: - "attack_damage_perk" - "attack_speed_perk" @@ -39,6 +79,10 @@ rarities: epic: display_name: "Épique" color: "" + symbol: "✷" + max_per_item: 2 + craft_sound: "entity.player.levelup" + craft_announce: "[★] %player% a créé %perk% (%rarity%) !" perks: - "lifesteal_perk" - "glass_cannon_perk" @@ -46,5 +90,9 @@ rarities: legendary: display_name: "Légendaire" color: "" + symbol: "✪" + max_per_item: 1 + craft_sound: "ui.toast.challenge_complete" + craft_announce: "[★] %player% vient de créer %perk% (%rarity%) !" perks: - "warden_perk" From 38280da99a93470ca8a1b3d76fd37b27acb321c1 Mon Sep 17 00:00:00 2001 From: Antidino35 Date: Sat, 24 Oct 2026 09:42:18 +0200 Subject: [PATCH 2/2] fix(rarity) : missing import --- .../java/fr/antidino/smp/modules/perk/rarity/RarityManager.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/fr/antidino/smp/modules/perk/rarity/RarityManager.java b/src/main/java/fr/antidino/smp/modules/perk/rarity/RarityManager.java index e24e45a..b3de984 100644 --- a/src/main/java/fr/antidino/smp/modules/perk/rarity/RarityManager.java +++ b/src/main/java/fr/antidino/smp/modules/perk/rarity/RarityManager.java @@ -2,6 +2,7 @@ import fr.antidino.smp.CorePlugin; import fr.antidino.smp.modules.perk.AbstractPerk; +import fr.antidino.smp.modules.perk.PerkEntry; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.minimessage.MiniMessage; import org.bukkit.Bukkit;