diff --git a/pom.xml b/pom.xml
index ca6490a..4760ce7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
fr.votenetwork
votenetwork-parent
- 1.0.1
+ 1.0.2
pom
diff --git a/spigot/pom.xml b/spigot/pom.xml
index f7e5135..bedc7e0 100644
--- a/spigot/pom.xml
+++ b/spigot/pom.xml
@@ -7,7 +7,7 @@
fr.votenetwork
votenetwork-parent
- 1.0.1
+ 1.0.2
votenetwork-spigot
diff --git a/spigot/src/main/java/fr/votenetwork/spigot/ClaimAction.java b/spigot/src/main/java/fr/votenetwork/spigot/ClaimAction.java
new file mode 100644
index 0000000..4ab403d
--- /dev/null
+++ b/spigot/src/main/java/fr/votenetwork/spigot/ClaimAction.java
@@ -0,0 +1,54 @@
+package fr.votenetwork.spigot;
+
+import fr.votenetwork.spigot.storage.PendingVoteStore;
+import fr.votenetwork.spigot.voteparty.VoteParty;
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Player;
+
+/**
+ * Reclame les votes en attente d'un joueur et distribue les recompenses. Partagee entre la
+ * commande /claim (texte) et le menu de reclamation visuel (/votemenu).
+ */
+public final class ClaimAction {
+
+ private ClaimAction() {
+ }
+
+ public static void claim(VoteNetworkSpigot plugin, VoteConfig config, PendingVoteStore pendingVoteStore,
+ VoteParty voteParty, Player player) {
+ Bukkit.getScheduler().runTaskAsynchronously(plugin, () ->
+ pendingVoteStore.fetchAndClear(
+ player.getUniqueId(),
+ pendingVotes -> Bukkit.getScheduler().runTask(plugin, () -> apply(plugin, config, voteParty, player, pendingVotes)),
+ exception -> Bukkit.getScheduler().runTask(plugin, () -> {
+ plugin.getLogger().severe("Erreur reclamation pour " + player.getName() + ": " + exception.getMessage());
+ player.sendMessage(config.getMessageMaintenance());
+ })
+ )
+ );
+ }
+
+ private static void apply(VoteNetworkSpigot plugin, VoteConfig config, VoteParty voteParty, Player player, int pendingVotes) {
+ if (pendingVotes <= 0) {
+ player.sendMessage(config.getMessageClaimEmpty());
+ return;
+ }
+
+ for (int i = 0; i < pendingVotes; i++) {
+ for (String cmd : config.getVoteRewardCommands()) {
+ String parsed = cmd.replace("%player%", player.getName());
+ Bukkit.dispatchCommand(Bukkit.getConsoleSender(), parsed);
+ }
+ }
+
+ voteParty.addVotes(pendingVotes);
+
+ player.sendMessage(config.getMessageClaimSuccess().replace("%amount%", String.valueOf(pendingVotes)));
+
+ if (config.isVoteBroadcastEnabled()) {
+ Bukkit.broadcastMessage(config.getVoteBroadcastMessage()
+ .replace("%player%", player.getName())
+ .replace("%amount%", String.valueOf(pendingVotes)));
+ }
+ }
+}
diff --git a/spigot/src/main/java/fr/votenetwork/spigot/VoteConfig.java b/spigot/src/main/java/fr/votenetwork/spigot/VoteConfig.java
index 39e1d83..5670723 100644
--- a/spigot/src/main/java/fr/votenetwork/spigot/VoteConfig.java
+++ b/spigot/src/main/java/fr/votenetwork/spigot/VoteConfig.java
@@ -197,6 +197,108 @@ public class VoteConfig {
return sites;
}
+ // --- Menu principal (/votemenu) ---
+
+ public String getMainMenuTitle() {
+ return colorize(cfg().getString("gui.main-menu.title", "&8VoteNetwork - Menu"));
+ }
+
+ public List getMainMenuHeadLore() {
+ return colorizeList(cfg().getStringList("gui.main-menu.head-lore"));
+ }
+
+ public String getMainMenuVoteItemMaterial() {
+ return cfg().getString("gui.main-menu.vote-item.material", "PAPER");
+ }
+
+ public String getMainMenuVoteItemName() {
+ return colorize(cfg().getString("gui.main-menu.vote-item.name", "&aVoter"));
+ }
+
+ public List getMainMenuVoteItemLore() {
+ return colorizeList(cfg().getStringList("gui.main-menu.vote-item.lore"));
+ }
+
+ public String getMainMenuClaimItemMaterial() {
+ return cfg().getString("gui.main-menu.claim-item.material", "CHEST");
+ }
+
+ public String getMainMenuClaimItemName() {
+ return colorize(cfg().getString("gui.main-menu.claim-item.name", "&eMes votes en attente"));
+ }
+
+ public List getMainMenuClaimItemLore() {
+ return colorizeList(cfg().getStringList("gui.main-menu.claim-item.lore"));
+ }
+
+ public String getMainMenuAdminItemMaterial() {
+ return cfg().getString("gui.main-menu.admin-item.material", "REDSTONE");
+ }
+
+ public String getMainMenuAdminItemName() {
+ return colorize(cfg().getString("gui.main-menu.admin-item.name", "&cAdministration"));
+ }
+
+ public List getMainMenuAdminItemLore() {
+ return colorizeList(cfg().getStringList("gui.main-menu.admin-item.lore"));
+ }
+
+ // --- Menu de reclamation (/claim visuel) ---
+
+ public String getClaimMenuTitle() {
+ return colorize(cfg().getString("gui.claim-menu.title", "&8VoteNetwork - Reclamer"));
+ }
+
+ public String getClaimMenuLoadingLore() {
+ return colorize(cfg().getString("gui.claim-menu.loading-lore", "&7Chargement..."));
+ }
+
+ public String getClaimMenuAvailableMaterial() {
+ return cfg().getString("gui.claim-menu.available.material", "CHEST");
+ }
+
+ public String getClaimMenuAvailableName() {
+ return colorize(cfg().getString("gui.claim-menu.available.name", "&e%amount% vote(s) en attente"));
+ }
+
+ public List getClaimMenuAvailableLore() {
+ return colorizeList(cfg().getStringList("gui.claim-menu.available.lore"));
+ }
+
+ public String getClaimMenuEmptyMaterial() {
+ return cfg().getString("gui.claim-menu.empty.material", "BARRIER");
+ }
+
+ public String getClaimMenuEmptyName() {
+ return colorize(cfg().getString("gui.claim-menu.empty.name", "&7Aucun vote en attente"));
+ }
+
+ public List getClaimMenuEmptyLore() {
+ return colorizeList(cfg().getStringList("gui.claim-menu.empty.lore"));
+ }
+
+ // --- Menu d'administration (/votemenu, reserve aux ops) ---
+
+ public String getAdminMenuTitle() {
+ return colorize(cfg().getString("gui.admin-menu.title", "&8VoteNetwork - Administration"));
+ }
+
+ public String getAdminMenuItemMaterial() {
+ return cfg().getString("gui.admin-menu.item.material", "LEVER");
+ }
+
+ public String getAdminMenuItemNameEnabled() {
+ return colorize(cfg().getString("gui.admin-menu.item.name-maintenance-on", "&c&lMaintenance : ACTIVEE"));
+ }
+
+ public String getAdminMenuItemNameDisabled() {
+ return colorize(cfg().getString("gui.admin-menu.item.name-maintenance-off", "&a&lMaintenance : DESACTIVEE"));
+ }
+
+ public List getAdminMenuItemLore() {
+ return colorizeList(cfg().getStringList("gui.admin-menu.item.lore"));
+ }
+
private String colorize(String input) {
return input == null ? "" : ChatColor.translateAlternateColorCodes('&', input);
}
diff --git a/spigot/src/main/java/fr/votenetwork/spigot/VoteNetworkSpigot.java b/spigot/src/main/java/fr/votenetwork/spigot/VoteNetworkSpigot.java
index 1b76606..5509fbf 100644
--- a/spigot/src/main/java/fr/votenetwork/spigot/VoteNetworkSpigot.java
+++ b/spigot/src/main/java/fr/votenetwork/spigot/VoteNetworkSpigot.java
@@ -6,6 +6,8 @@ import fr.votenetwork.spigot.api.VoteNetworkAPIImpl;
import fr.votenetwork.spigot.command.ClaimCommand;
import fr.votenetwork.spigot.command.VoteAdminCommand;
import fr.votenetwork.spigot.command.VoteGuiCommand;
+import fr.votenetwork.spigot.command.VoteMenuCommand;
+import fr.votenetwork.spigot.gui.VoteMenuListener;
import fr.votenetwork.spigot.gui.VotePartyGuiListener;
import fr.votenetwork.spigot.listener.VoteMessageListener;
import fr.votenetwork.spigot.placeholder.VoteNetworkPlaceholders;
@@ -58,7 +60,10 @@ public class VoteNetworkSpigot extends JavaPlugin {
getCommand("claim").setExecutor(new ClaimCommand(this, voteConfig, pendingVoteStore, voteParty, maintenanceState));
getCommand("vote").setExecutor(new VoteAdminCommand(this, voteConfig, pendingVoteStore, maintenanceState));
getCommand("votegui").setExecutor(new VoteGuiCommand(voteConfig, voteParty));
+ getCommand("votemenu").setExecutor(new VoteMenuCommand(voteConfig, voteParty));
getServer().getPluginManager().registerEvents(new VotePartyGuiListener(voteConfig), this);
+ getServer().getPluginManager().registerEvents(
+ new VoteMenuListener(this, voteConfig, pendingVoteStore, voteParty, maintenanceState), this);
VoteNetworkAPI api = new VoteNetworkAPIImpl(this, voteConfig, pendingVoteStore, voteParty, maintenanceState);
VoteNetworkAPIHolder.set(api);
diff --git a/spigot/src/main/java/fr/votenetwork/spigot/command/ClaimCommand.java b/spigot/src/main/java/fr/votenetwork/spigot/command/ClaimCommand.java
index 7a1db03..2d2b3d9 100644
--- a/spigot/src/main/java/fr/votenetwork/spigot/command/ClaimCommand.java
+++ b/spigot/src/main/java/fr/votenetwork/spigot/command/ClaimCommand.java
@@ -1,11 +1,11 @@
package fr.votenetwork.spigot.command;
+import fr.votenetwork.spigot.ClaimAction;
import fr.votenetwork.spigot.MaintenanceState;
import fr.votenetwork.spigot.VoteNetworkSpigot;
import fr.votenetwork.spigot.VoteConfig;
import fr.votenetwork.spigot.storage.PendingVoteStore;
import fr.votenetwork.spigot.voteparty.VoteParty;
-import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
@@ -42,41 +42,7 @@ public class ClaimCommand implements CommandExecutor {
return true;
}
- Bukkit.getScheduler().runTaskAsynchronously(plugin, () ->
- pendingVoteStore.fetchAndClear(
- player.getUniqueId(),
- pendingVotes -> Bukkit.getScheduler().runTask(plugin, () -> applyClaim(player, pendingVotes)),
- exception -> Bukkit.getScheduler().runTask(plugin, () -> {
- plugin.getLogger().severe("Erreur /claim pour " + player.getName() + ": " + exception.getMessage());
- player.sendMessage(config.getMessageMaintenance());
- })
- )
- );
-
+ ClaimAction.claim(plugin, config, pendingVoteStore, voteParty, player);
return true;
}
-
- private void applyClaim(Player player, int pendingVotes) {
- if (pendingVotes <= 0) {
- player.sendMessage(config.getMessageClaimEmpty());
- return;
- }
-
- for (int i = 0; i < pendingVotes; i++) {
- for (String cmd : config.getVoteRewardCommands()) {
- String parsed = cmd.replace("%player%", player.getName());
- Bukkit.dispatchCommand(Bukkit.getConsoleSender(), parsed);
- }
- }
-
- voteParty.addVotes(pendingVotes);
-
- player.sendMessage(config.getMessageClaimSuccess().replace("%amount%", String.valueOf(pendingVotes)));
-
- if (config.isVoteBroadcastEnabled()) {
- Bukkit.broadcastMessage(config.getVoteBroadcastMessage()
- .replace("%player%", player.getName())
- .replace("%amount%", String.valueOf(pendingVotes)));
- }
- }
}
diff --git a/spigot/src/main/java/fr/votenetwork/spigot/command/VoteMenuCommand.java b/spigot/src/main/java/fr/votenetwork/spigot/command/VoteMenuCommand.java
new file mode 100644
index 0000000..e758b76
--- /dev/null
+++ b/spigot/src/main/java/fr/votenetwork/spigot/command/VoteMenuCommand.java
@@ -0,0 +1,31 @@
+package fr.votenetwork.spigot.command;
+
+import fr.votenetwork.spigot.VoteConfig;
+import fr.votenetwork.spigot.gui.MainMenuGui;
+import fr.votenetwork.spigot.voteparty.VoteParty;
+import org.bukkit.ChatColor;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandExecutor;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+
+public class VoteMenuCommand implements CommandExecutor {
+
+ private final VoteConfig config;
+ private final VoteParty voteParty;
+
+ public VoteMenuCommand(VoteConfig config, VoteParty voteParty) {
+ this.config = config;
+ this.voteParty = voteParty;
+ }
+
+ @Override
+ public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
+ if (!(sender instanceof Player player)) {
+ sender.sendMessage(ChatColor.RED + "Commande reservee aux joueurs.");
+ return true;
+ }
+ MainMenuGui.open(player, config, voteParty);
+ return true;
+ }
+}
diff --git a/spigot/src/main/java/fr/votenetwork/spigot/gui/AdminMenuGui.java b/spigot/src/main/java/fr/votenetwork/spigot/gui/AdminMenuGui.java
new file mode 100644
index 0000000..d193645
--- /dev/null
+++ b/spigot/src/main/java/fr/votenetwork/spigot/gui/AdminMenuGui.java
@@ -0,0 +1,47 @@
+package fr.votenetwork.spigot.gui;
+
+import fr.votenetwork.spigot.MaintenanceState;
+import fr.votenetwork.spigot.VoteConfig;
+import org.bukkit.Bukkit;
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.Inventory;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
+
+import java.util.Locale;
+
+/** Ops-only screen (permission votenetwork.admin) to toggle maintenance mode with one click. */
+public final class AdminMenuGui {
+
+ private AdminMenuGui() {
+ }
+
+ public static void open(Player player, VoteConfig config, MaintenanceState maintenanceState) {
+ AdminMenuHolder holder = new AdminMenuHolder();
+ Inventory inventory = Bukkit.createInventory(holder, 27, config.getAdminMenuTitle());
+ holder.setInventory(inventory);
+
+ inventory.setItem(AdminMenuHolder.ITEM_SLOT, buildItem(config, maintenanceState.isStopped()));
+
+ player.openInventory(inventory);
+ }
+
+ public static ItemStack buildItem(VoteConfig config, boolean stopped) {
+ Material material;
+ try {
+ material = Material.valueOf(config.getAdminMenuItemMaterial().toUpperCase(Locale.ROOT));
+ } catch (IllegalArgumentException e) {
+ material = Material.LEVER;
+ }
+
+ ItemStack item = new ItemStack(material);
+ ItemMeta meta = item.getItemMeta();
+ if (meta != null) {
+ meta.setDisplayName(stopped ? config.getAdminMenuItemNameEnabled() : config.getAdminMenuItemNameDisabled());
+ meta.setLore(config.getAdminMenuItemLore());
+ item.setItemMeta(meta);
+ }
+ return item;
+ }
+}
diff --git a/spigot/src/main/java/fr/votenetwork/spigot/gui/AdminMenuHolder.java b/spigot/src/main/java/fr/votenetwork/spigot/gui/AdminMenuHolder.java
new file mode 100644
index 0000000..2961e51
--- /dev/null
+++ b/spigot/src/main/java/fr/votenetwork/spigot/gui/AdminMenuHolder.java
@@ -0,0 +1,21 @@
+package fr.votenetwork.spigot.gui;
+
+import org.bukkit.inventory.Inventory;
+import org.bukkit.inventory.InventoryHolder;
+
+/** Marker holder for the ops-only administration screen (toggle maintenance mode). */
+public class AdminMenuHolder implements InventoryHolder {
+
+ public static final int ITEM_SLOT = 13;
+
+ private Inventory inventory;
+
+ @Override
+ public Inventory getInventory() {
+ return inventory;
+ }
+
+ public void setInventory(Inventory inventory) {
+ this.inventory = inventory;
+ }
+}
diff --git a/spigot/src/main/java/fr/votenetwork/spigot/gui/ClaimMenuGui.java b/spigot/src/main/java/fr/votenetwork/spigot/gui/ClaimMenuGui.java
new file mode 100644
index 0000000..553a8f1
--- /dev/null
+++ b/spigot/src/main/java/fr/votenetwork/spigot/gui/ClaimMenuGui.java
@@ -0,0 +1,80 @@
+package fr.votenetwork.spigot.gui;
+
+import fr.votenetwork.spigot.VoteConfig;
+import fr.votenetwork.spigot.VoteNetworkSpigot;
+import fr.votenetwork.spigot.storage.PendingVoteStore;
+import org.bukkit.Bukkit;
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.Inventory;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
+
+import java.util.Locale;
+
+/**
+ * Visual counterpart of /claim: shows how many votes are waiting for the player, resolved
+ * asynchronously via {@link PendingVoteStore#peek}, and lets them claim by clicking the item.
+ */
+public final class ClaimMenuGui {
+
+ private ClaimMenuGui() {
+ }
+
+ public static void open(Player player, VoteNetworkSpigot plugin, VoteConfig config, PendingVoteStore pendingVoteStore) {
+ ClaimMenuHolder holder = new ClaimMenuHolder();
+ Inventory inventory = Bukkit.createInventory(holder, 27, config.getClaimMenuTitle());
+ holder.setInventory(inventory);
+
+ ItemStack loading = new ItemStack(Material.CLOCK);
+ ItemMeta loadingMeta = loading.getItemMeta();
+ if (loadingMeta != null) {
+ loadingMeta.setDisplayName(config.getClaimMenuLoadingLore());
+ loading.setItemMeta(loadingMeta);
+ }
+ inventory.setItem(ClaimMenuHolder.ITEM_SLOT, loading);
+
+ player.openInventory(inventory);
+
+ Bukkit.getScheduler().runTaskAsynchronously(plugin, () ->
+ pendingVoteStore.peek(
+ player.getUniqueId(),
+ amount -> Bukkit.getScheduler().runTask(plugin, () -> {
+ holder.setPendingVotes(amount);
+ if (player.getOpenInventory().getTopInventory().getHolder() == holder) {
+ inventory.setItem(ClaimMenuHolder.ITEM_SLOT, buildItem(config, amount));
+ }
+ }),
+ exception -> Bukkit.getScheduler().runTask(plugin, () -> {
+ plugin.getLogger().severe("Erreur menu reclamation pour " + player.getName() + ": " + exception.getMessage());
+ player.closeInventory();
+ })
+ )
+ );
+ }
+
+ public static ItemStack buildItem(VoteConfig config, int amount) {
+ boolean available = amount > 0;
+ String materialName = available ? config.getClaimMenuAvailableMaterial() : config.getClaimMenuEmptyMaterial();
+ String name = available
+ ? config.getClaimMenuAvailableName().replace("%amount%", String.valueOf(amount))
+ : config.getClaimMenuEmptyName();
+ java.util.List lore = available ? config.getClaimMenuAvailableLore() : config.getClaimMenuEmptyLore();
+
+ Material material;
+ try {
+ material = Material.valueOf(materialName.toUpperCase(Locale.ROOT));
+ } catch (IllegalArgumentException e) {
+ material = Material.CHEST;
+ }
+
+ ItemStack item = new ItemStack(material);
+ ItemMeta meta = item.getItemMeta();
+ if (meta != null) {
+ meta.setDisplayName(name);
+ meta.setLore(lore);
+ item.setItemMeta(meta);
+ }
+ return item;
+ }
+}
diff --git a/spigot/src/main/java/fr/votenetwork/spigot/gui/ClaimMenuHolder.java b/spigot/src/main/java/fr/votenetwork/spigot/gui/ClaimMenuHolder.java
new file mode 100644
index 0000000..3a35c72
--- /dev/null
+++ b/spigot/src/main/java/fr/votenetwork/spigot/gui/ClaimMenuHolder.java
@@ -0,0 +1,31 @@
+package fr.votenetwork.spigot.gui;
+
+import org.bukkit.inventory.Inventory;
+import org.bukkit.inventory.InventoryHolder;
+
+/** Marker holder for the visual /claim screen opened from the main menu. */
+public class ClaimMenuHolder implements InventoryHolder {
+
+ public static final int ITEM_SLOT = 13;
+
+ private Inventory inventory;
+ private volatile int pendingVotes = -1;
+
+ @Override
+ public Inventory getInventory() {
+ return inventory;
+ }
+
+ public void setInventory(Inventory inventory) {
+ this.inventory = inventory;
+ }
+
+ /** -1 while the async lookup is still pending, >= 0 once resolved. */
+ public int getPendingVotes() {
+ return pendingVotes;
+ }
+
+ public void setPendingVotes(int pendingVotes) {
+ this.pendingVotes = pendingVotes;
+ }
+}
diff --git a/spigot/src/main/java/fr/votenetwork/spigot/gui/MainMenuGui.java b/spigot/src/main/java/fr/votenetwork/spigot/gui/MainMenuGui.java
new file mode 100644
index 0000000..a2c0b78
--- /dev/null
+++ b/spigot/src/main/java/fr/votenetwork/spigot/gui/MainMenuGui.java
@@ -0,0 +1,74 @@
+package fr.votenetwork.spigot.gui;
+
+import fr.votenetwork.spigot.VoteConfig;
+import fr.votenetwork.spigot.voteparty.VoteParty;
+import org.bukkit.Bukkit;
+import org.bukkit.ChatColor;
+import org.bukkit.Material;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.Inventory;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
+import org.bukkit.inventory.meta.SkullMeta;
+
+/**
+ * /votemenu entry point: player head with VoteParty progress, plus navigation to the vote-site
+ * links (/votegui), the visual claim menu, and (ops only) the administration menu.
+ */
+public final class MainMenuGui {
+
+ private MainMenuGui() {
+ }
+
+ public static void open(Player player, VoteConfig config, VoteParty voteParty) {
+ MainMenuHolder holder = new MainMenuHolder();
+ Inventory inventory = Bukkit.createInventory(holder, 27, config.getMainMenuTitle());
+ holder.setInventory(inventory);
+
+ int required = config.getVotePartyVotesRequired();
+ int current = Math.min(voteParty.getCurrentVotes(), required);
+
+ ItemStack head = new ItemStack(Material.PLAYER_HEAD);
+ SkullMeta skullMeta = (SkullMeta) head.getItemMeta();
+ if (skullMeta != null) {
+ skullMeta.setOwningPlayer(player);
+ skullMeta.setDisplayName(ChatColor.translateAlternateColorCodes('&', "&b&l" + player.getName()));
+ skullMeta.setLore(config.getMainMenuHeadLore().stream()
+ .map(line -> line.replace("%player%", player.getName())
+ .replace("%current%", String.valueOf(current))
+ .replace("%required%", String.valueOf(required)))
+ .toList());
+ head.setItemMeta(skullMeta);
+ }
+ inventory.setItem(4, head);
+
+ inventory.setItem(MainMenuHolder.VOTE_SLOT, buildItem(
+ config.getMainMenuVoteItemMaterial(), config.getMainMenuVoteItemName(), config.getMainMenuVoteItemLore()));
+ inventory.setItem(MainMenuHolder.CLAIM_SLOT, buildItem(
+ config.getMainMenuClaimItemMaterial(), config.getMainMenuClaimItemName(), config.getMainMenuClaimItemLore()));
+
+ if (player.hasPermission("votenetwork.admin")) {
+ inventory.setItem(MainMenuHolder.ADMIN_SLOT, buildItem(
+ config.getMainMenuAdminItemMaterial(), config.getMainMenuAdminItemName(), config.getMainMenuAdminItemLore()));
+ }
+
+ player.openInventory(inventory);
+ }
+
+ private static ItemStack buildItem(String materialName, String name, java.util.List lore) {
+ Material material;
+ try {
+ material = Material.valueOf(materialName.toUpperCase(java.util.Locale.ROOT));
+ } catch (IllegalArgumentException e) {
+ material = Material.PAPER;
+ }
+ ItemStack item = new ItemStack(material);
+ ItemMeta meta = item.getItemMeta();
+ if (meta != null) {
+ meta.setDisplayName(name);
+ meta.setLore(lore);
+ item.setItemMeta(meta);
+ }
+ return item;
+ }
+}
diff --git a/spigot/src/main/java/fr/votenetwork/spigot/gui/MainMenuHolder.java b/spigot/src/main/java/fr/votenetwork/spigot/gui/MainMenuHolder.java
new file mode 100644
index 0000000..eff9c54
--- /dev/null
+++ b/spigot/src/main/java/fr/votenetwork/spigot/gui/MainMenuHolder.java
@@ -0,0 +1,23 @@
+package fr.votenetwork.spigot.gui;
+
+import org.bukkit.inventory.Inventory;
+import org.bukkit.inventory.InventoryHolder;
+
+/** Marker holder for the /votemenu main screen. */
+public class MainMenuHolder implements InventoryHolder {
+
+ public static final int VOTE_SLOT = 11;
+ public static final int CLAIM_SLOT = 13;
+ public static final int ADMIN_SLOT = 15;
+
+ private Inventory inventory;
+
+ @Override
+ public Inventory getInventory() {
+ return inventory;
+ }
+
+ public void setInventory(Inventory inventory) {
+ this.inventory = inventory;
+ }
+}
diff --git a/spigot/src/main/java/fr/votenetwork/spigot/gui/VoteMenuListener.java b/spigot/src/main/java/fr/votenetwork/spigot/gui/VoteMenuListener.java
new file mode 100644
index 0000000..fde6a2a
--- /dev/null
+++ b/spigot/src/main/java/fr/votenetwork/spigot/gui/VoteMenuListener.java
@@ -0,0 +1,83 @@
+package fr.votenetwork.spigot.gui;
+
+import fr.votenetwork.spigot.ClaimAction;
+import fr.votenetwork.spigot.MaintenanceState;
+import fr.votenetwork.spigot.VoteConfig;
+import fr.votenetwork.spigot.VoteNetworkSpigot;
+import fr.votenetwork.spigot.storage.PendingVoteStore;
+import fr.votenetwork.spigot.voteparty.VoteParty;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.inventory.InventoryClickEvent;
+
+/** Click handling for the main/claim/admin menus (/votemenu tree). */
+public class VoteMenuListener implements Listener {
+
+ private final VoteNetworkSpigot plugin;
+ private final VoteConfig config;
+ private final PendingVoteStore pendingVoteStore;
+ private final VoteParty voteParty;
+ private final MaintenanceState maintenanceState;
+
+ public VoteMenuListener(VoteNetworkSpigot plugin, VoteConfig config, PendingVoteStore pendingVoteStore,
+ VoteParty voteParty, MaintenanceState maintenanceState) {
+ this.plugin = plugin;
+ this.config = config;
+ this.pendingVoteStore = pendingVoteStore;
+ this.voteParty = voteParty;
+ this.maintenanceState = maintenanceState;
+ }
+
+ @EventHandler
+ public void onInventoryClick(InventoryClickEvent event) {
+ Object rawHolder = event.getInventory().getHolder();
+ if (!(event.getWhoClicked() instanceof Player player)) {
+ return;
+ }
+
+ if (rawHolder instanceof MainMenuHolder) {
+ event.setCancelled(true);
+ handleMainMenuClick(player, event.getRawSlot());
+ } else if (rawHolder instanceof ClaimMenuHolder holder) {
+ event.setCancelled(true);
+ handleClaimMenuClick(player, holder, event.getRawSlot());
+ } else if (rawHolder instanceof AdminMenuHolder) {
+ event.setCancelled(true);
+ handleAdminMenuClick(player, event.getRawSlot());
+ }
+ }
+
+ private void handleMainMenuClick(Player player, int slot) {
+ if (slot == MainMenuHolder.VOTE_SLOT) {
+ VotePartyGui.open(player, config, voteParty);
+ } else if (slot == MainMenuHolder.CLAIM_SLOT) {
+ ClaimMenuGui.open(player, plugin, config, pendingVoteStore);
+ } else if (slot == MainMenuHolder.ADMIN_SLOT && player.hasPermission("votenetwork.admin")) {
+ AdminMenuGui.open(player, config, maintenanceState);
+ }
+ }
+
+ private void handleClaimMenuClick(Player player, ClaimMenuHolder holder, int slot) {
+ if (slot != ClaimMenuHolder.ITEM_SLOT || holder.getPendingVotes() < 0) {
+ return;
+ }
+ if (maintenanceState.isStopped()) {
+ player.closeInventory();
+ player.sendMessage(config.getMessageMaintenance());
+ return;
+ }
+ player.closeInventory();
+ ClaimAction.claim(plugin, config, pendingVoteStore, voteParty, player);
+ }
+
+ private void handleAdminMenuClick(Player player, int slot) {
+ if (slot != AdminMenuHolder.ITEM_SLOT || !player.hasPermission("votenetwork.admin")) {
+ return;
+ }
+ boolean newState = !maintenanceState.isStopped();
+ maintenanceState.setStopped(newState);
+ player.sendMessage(newState ? config.getMessageMaintenanceEnabled() : config.getMessageMaintenanceDisabled());
+ player.getOpenInventory().getTopInventory().setItem(AdminMenuHolder.ITEM_SLOT, AdminMenuGui.buildItem(config, newState));
+ }
+}
diff --git a/spigot/src/main/resources/config.yml b/spigot/src/main/resources/config.yml
index 38ee5d1..c8c856d 100644
--- a/spigot/src/main/resources/config.yml
+++ b/spigot/src/main/resources/config.yml
@@ -71,6 +71,55 @@ gui:
lore:
- "&7Clique pour voter !"
+# Menu principal /votemenu : profil du joueur, progression VoteParty, et boutons de
+# navigation vers le menu de vote (/votegui), de reclamation et (pour les ops) d'administration.
+ main-menu:
+ title: "&8VoteNetwork - Menu"
+ head-lore:
+ - "&7Joueur : &f%player%"
+ - "&7Progression VoteParty :"
+ - "&f%current%&7/&f%required% &7votes"
+ vote-item:
+ material: PAPER
+ name: "&aVoter"
+ lore:
+ - "&7Clique pour voir les liens de vote."
+ claim-item:
+ material: CHEST
+ name: "&eMes votes en attente"
+ lore:
+ - "&7Clique pour reclamer tes votes stockes."
+ admin-item:
+ material: REDSTONE
+ name: "&cAdministration"
+ lore:
+ - "&7Clique pour gerer le mode maintenance."
+
+ # Menu de reclamation visuel (equivalent GUI de /claim).
+ claim-menu:
+ title: "&8VoteNetwork - Reclamer"
+ loading-lore: "&7Chargement..."
+ available:
+ material: CHEST
+ name: "&e%amount% vote(s) en attente"
+ lore:
+ - "&7Clique pour reclamer !"
+ empty:
+ material: BARRIER
+ name: "&7Aucun vote en attente"
+ lore:
+ - "&7Reviens plus tard."
+
+ # Menu d'administration (reserve aux ops, permission votenetwork.admin).
+ admin-menu:
+ title: "&8VoteNetwork - Administration"
+ item:
+ material: LEVER
+ name-maintenance-on: "&c&lMaintenance : ACTIVEE"
+ name-maintenance-off: "&a&lMaintenance : DESACTIVEE"
+ lore:
+ - "&7Clique pour basculer le mode maintenance."
+
messages:
direct-vote: "&a[Vote] &fMerci pour ton vote !"
claim-success: "&a[Vote] &fVous avez recupere &e%amount% &fvote(s) en attente !"
diff --git a/spigot/src/main/resources/plugin.yml b/spigot/src/main/resources/plugin.yml
index aaa65ab..3db4a4e 100644
--- a/spigot/src/main/resources/plugin.yml
+++ b/spigot/src/main/resources/plugin.yml
@@ -17,6 +17,9 @@ commands:
votegui:
description: Ouvre le GUI de progression du VoteParty avec les liens de vote.
usage: /votegui
+ votemenu:
+ description: Ouvre le menu principal (profil, votes en attente, administration).
+ usage: /votemenu
permissions:
votenetwork.admin:
diff --git a/velocity/pom.xml b/velocity/pom.xml
index c858217..ecb63f5 100644
--- a/velocity/pom.xml
+++ b/velocity/pom.xml
@@ -7,7 +7,7 @@
fr.votenetwork
votenetwork-parent
- 1.0.1
+ 1.0.2
votenetwork-velocity
diff --git a/velocity/src/main/java/fr/votenetwork/velocity/VoteNetworkVelocity.java b/velocity/src/main/java/fr/votenetwork/velocity/VoteNetworkVelocity.java
index a2dc281..cad1934 100644
--- a/velocity/src/main/java/fr/votenetwork/velocity/VoteNetworkVelocity.java
+++ b/velocity/src/main/java/fr/votenetwork/velocity/VoteNetworkVelocity.java
@@ -14,7 +14,7 @@ import java.nio.file.Path;
@Plugin(
id = "votenetwork",
name = "VoteNetwork",
- version = "1.0.1",
+ version = "1.0.2",
description = "Systeme de vote reseau Velocity <-> Spigot",
authors = {"Sar_Tron"}
)