diff --git a/LobbySystem/src/de/steamwar/lobby/particle/gui/BottomBar.java b/LobbySystem/src/de/steamwar/lobby/particle/gui/BottomBar.java
new file mode 100644
index 00000000..71637ad1
--- /dev/null
+++ b/LobbySystem/src/de/steamwar/lobby/particle/gui/BottomBar.java
@@ -0,0 +1,59 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2026 SteamWar.de-Serverteam
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package de.steamwar.lobby.particle.gui;
+
+import de.steamwar.inventory.SWInventory;
+import de.steamwar.inventory.SWItem;
+import lombok.experimental.UtilityClass;
+import org.bukkit.Material;
+
+// Every particle menu puts the same button in the same column of its last row
+@UtilityClass
+class BottomBar {
+
+ // Taken by the page arrows of list menus
+ final int PREVIOUS_PAGE = 0;
+ final int BACK = 1;
+ final int USE = 2;
+ final int ADD = 3;
+ final int RENAME = 4;
+ final int ICON = 5;
+ final int COST = 6;
+ // Delete in editors, reset in value screens; no menu has both
+ final int DELETE = 7;
+ final int RESET = 7;
+ final int NEXT_PAGE = 8;
+
+ int position(int inventorySize, int column) {
+ return inventorySize - 9 + column;
+ }
+
+ // Marks the columns this menu does not use, so the buttons it does use stay recognisable by position.
+ // List menus keep the outer columns free for the page arrows they add when opened.
+ void fillUnused(SWInventory inventory, int inventorySize, boolean listMenu) {
+ for (int column = listMenu ? 1 : 0; column < (listMenu ? 8 : 9); column++) {
+ int position = position(inventorySize, column);
+ if (!inventory.hasItem(position)) {
+ inventory.setItem(position, new SWItem(Material.BARRIER, "§8-", clickType -> {
+ }));
+ }
+ }
+ }
+}
diff --git a/LobbySystem/src/de/steamwar/lobby/particle/gui/EntryEditor.java b/LobbySystem/src/de/steamwar/lobby/particle/gui/EntryEditor.java
index 866cf37b..59448f5c 100644
--- a/LobbySystem/src/de/steamwar/lobby/particle/gui/EntryEditor.java
+++ b/LobbySystem/src/de/steamwar/lobby/particle/gui/EntryEditor.java
@@ -50,9 +50,10 @@ class EntryEditor {
private final int ROW_SHAPE = 2;
private final int ROW_TIMING = 3;
private final int ROW_CONDITIONS = 4;
- private final int BACK = 45;
- private final int COST = 49;
- private final int DELETE = 53;
+ private final int SIZE = 54;
+ private final int BACK = BottomBar.position(SIZE, BottomBar.BACK);
+ private final int COST = BottomBar.position(SIZE, BottomBar.COST);
+ private final int DELETE = BottomBar.position(SIZE, BottomBar.DELETE);
private final Map, Material> ICONS = Map.ofEntries(
Map.entry(COUNT, Material.GLOWSTONE_DUST),
@@ -97,7 +98,7 @@ class EntryEditor {
ParticleEntry entry = context.entry();
ParticleDesign design = entry.getDesign();
- SWInventory inventory = new SWInventory(player, 54, msg("PARTICLE_ENTRY_EDITOR", player, designName(design, player)));
+ SWInventory inventory = new SWInventory(player, SIZE, msg("PARTICLE_ENTRY_EDITOR", player, designName(design, player)));
header(inventory, ROW_DESIGN, Material.PAINTING, "PARTICLE_SECTION_DESIGN", player);
header(inventory, ROW_PARTICLE, Material.FIREWORK_STAR, "PARTICLE_SECTION_PARTICLE", player);
header(inventory, ROW_SHAPE, Material.ARMOR_STAND, "PARTICLE_SECTION_SHAPE", player);
@@ -128,7 +129,8 @@ class EntryEditor {
SlotEditor.save(player, index, slot);
SlotEditor.open(player, index);
}));
- SlotEditor.fill(inventory, 0, 54);
+ BottomBar.fillUnused(inventory, SIZE, false);
+ SlotEditor.fill(inventory, 0, SIZE);
inventory.open();
}
diff --git a/LobbySystem/src/de/steamwar/lobby/particle/gui/Pickers.java b/LobbySystem/src/de/steamwar/lobby/particle/gui/Pickers.java
index 90a70a01..28ba9bf0 100644
--- a/LobbySystem/src/de/steamwar/lobby/particle/gui/Pickers.java
+++ b/LobbySystem/src/de/steamwar/lobby/particle/gui/Pickers.java
@@ -39,7 +39,8 @@ import static de.steamwar.lobby.particle.gui.ParticleGuiText.*;
@UtilityClass
class Pickers {
- private final int BACK = 49;
+ private final int SIZE = 54;
+ private final int BACK = BottomBar.position(SIZE, BottomBar.BACK);
// Basic designs first, then rewards; locked rewards are shown with their requirement but cannot be picked.
// The current design, if any, glows.
@@ -104,6 +105,7 @@ class Pickers {
if (value != null) onPick.accept(value);
});
inventory.setItem(BACK, new SWItem(Material.ARROW, msg("PARTICLE_BACK", player), clickType -> onBack.run()));
+ BottomBar.fillUnused(inventory, SIZE, true);
inventory.open();
}
}
diff --git a/LobbySystem/src/de/steamwar/lobby/particle/gui/SlotEditor.java b/LobbySystem/src/de/steamwar/lobby/particle/gui/SlotEditor.java
index 4b672a88..a33d63aa 100644
--- a/LobbySystem/src/de/steamwar/lobby/particle/gui/SlotEditor.java
+++ b/LobbySystem/src/de/steamwar/lobby/particle/gui/SlotEditor.java
@@ -43,13 +43,14 @@ import static de.steamwar.lobby.particle.gui.ParticleGuiText.*;
@UtilityClass
public class SlotEditor {
- private final int BACK = 45;
- private final int USE = 46;
- private final int ADD_ENTRY = 47;
- private final int RENAME = 48;
- private final int ICON = 49;
- private final int COST = 50;
- private final int DELETE = 53;
+ private final int SIZE = 54;
+ private final int BACK = BottomBar.position(SIZE, BottomBar.BACK);
+ private final int USE = BottomBar.position(SIZE, BottomBar.USE);
+ private final int ADD_ENTRY = BottomBar.position(SIZE, BottomBar.ADD);
+ private final int RENAME = BottomBar.position(SIZE, BottomBar.RENAME);
+ private final int ICON = BottomBar.position(SIZE, BottomBar.ICON);
+ private final int COST = BottomBar.position(SIZE, BottomBar.COST);
+ private final int DELETE = BottomBar.position(SIZE, BottomBar.DELETE);
public void open(Player player, int index) {
LobbyPlayer lobbyPlayer = LobbyPlayer.getLobbyPlayer(player);
@@ -62,7 +63,7 @@ public class SlotEditor {
UnlockContext context = new UnlockContext(user);
boolean editable = ParticleSlots.requirement(index).test(context);
- SWInventory inventory = new SWInventory(player, 54, slotTitle(slot, user, player));
+ SWInventory inventory = new SWInventory(player, SIZE, slotTitle(slot, user, player));
List entries = slot.getEntries();
for (int i = 0; i < entries.size(); i++) {
ParticleEntry entry = entries.get(i);
@@ -80,9 +81,10 @@ public class SlotEditor {
inventory.setItem(BACK, new SWItem(Material.ARROW, msg("PARTICLE_BACK", player), clickType -> ParticleMenu.open(player)));
if (!editable) {
List lore = new ArrayList<>(requirement(ParticleSlots.requirement(index), player));
- inventory.setItem(ICON, new SWItem(Material.BARRIER, msg("PARTICLE_LOCKED", player), lore, false, clickType -> {
+ inventory.setItem(USE, new SWItem(Material.IRON_BARS, msg("PARTICLE_LOCKED", player), lore, false, clickType -> {
}));
- fill(inventory, 45, 54);
+ inventory.setItem(COST, costItem(player, user, slot));
+ BottomBar.fillUnused(inventory, SIZE, false);
inventory.open();
return;
}
@@ -138,7 +140,7 @@ public class SlotEditor {
lobbyPlayer.setSlot(index, null);
ParticleMenu.open(player);
}));
- fill(inventory, 45, 54);
+ BottomBar.fillUnused(inventory, SIZE, false);
inventory.open();
}
diff --git a/LobbySystem/src/de/steamwar/lobby/particle/gui/ValueEditors.java b/LobbySystem/src/de/steamwar/lobby/particle/gui/ValueEditors.java
index b248e501..89cbda37 100644
--- a/LobbySystem/src/de/steamwar/lobby/particle/gui/ValueEditors.java
+++ b/LobbySystem/src/de/steamwar/lobby/particle/gui/ValueEditors.java
@@ -41,7 +41,10 @@ import static de.steamwar.lobby.particle.gui.ParticleGuiText.*;
@UtilityClass
class ValueEditors {
- private final int BACK = 18;
+ private final int SIZE = 27;
+ private final int BACK = BottomBar.position(SIZE, BottomBar.BACK);
+ private final int ADD = BottomBar.position(SIZE, BottomBar.ADD);
+ private final int RESET = BottomBar.position(SIZE, BottomBar.RESET);
// Buttons step the value down or up; clicking the value itself lets the player type one
void editNumber(Player player, String title, Supplier current, double step, double min, double max, double defaultValue, boolean integer,
@@ -49,7 +52,7 @@ class ValueEditors {
double value = current.get();
Runnable reopen = () -> editNumber(player, title, current, step, min, max, defaultValue, integer, apply, back);
- SWInventory inventory = new SWInventory(player, 27, "§0" + strip(title));
+ SWInventory inventory = new SWInventory(player, SIZE, "§0" + strip(title));
inventory.setItem(10, stepItem(player, Material.RED_CONCRETE, -10 * step, step, value, min, max, integer, apply, reopen));
inventory.setItem(11, stepItem(player, Material.RED_STAINED_GLASS_PANE, -step, step, value, min, max, integer, apply, reopen));
inventory.setItem(15, stepItem(player, Material.LIME_STAINED_GLASS_PANE, step, step, value, min, max, integer, apply, reopen));
@@ -72,11 +75,12 @@ class ValueEditors {
}));
inventory.setItem(BACK, new SWItem(Material.ARROW, msg("PARTICLE_BACK", player), clickType -> back.run()));
- inventory.setItem(22, new SWItem(Material.STRUCTURE_VOID, msg("PARTICLE_VALUE_RESET", player, number(defaultValue)), clickType -> {
+ inventory.setItem(RESET, new SWItem(Material.STRUCTURE_VOID, msg("PARTICLE_VALUE_RESET", player, number(defaultValue)), clickType -> {
apply.test(defaultValue);
reopen.run();
}));
- SlotEditor.fill(inventory, 0, 27);
+ BottomBar.fillUnused(inventory, SIZE, false);
+ SlotEditor.fill(inventory, 0, SIZE);
inventory.open();
}
@@ -95,7 +99,7 @@ class ValueEditors {
List colors = current.get();
Runnable reopen = () -> editColors(player, current, maxColors, apply, back);
- SWInventory inventory = new SWInventory(player, 27, msg("PARTICLE_COLOR_EDITOR", player));
+ SWInventory inventory = new SWInventory(player, SIZE, msg("PARTICLE_COLOR_EDITOR", player));
for (int i = 0; i < colors.size() && i < 9; i++) {
int position = i;
Color color = colors.get(i);
@@ -114,18 +118,19 @@ class ValueEditors {
inventory.setItem(BACK, new SWItem(Material.ARROW, msg("PARTICLE_BACK", player), clickType -> back.run()));
if (colors.size() < maxColors) {
- inventory.setItem(21, new SWItem(Material.NETHER_STAR, msg("PARTICLE_COLOR_ADD", player), clickType -> Pickers.color(player, color -> {
+ inventory.setItem(ADD, new SWItem(Material.NETHER_STAR, msg("PARTICLE_COLOR_ADD", player), clickType -> Pickers.color(player, color -> {
List next = new ArrayList<>(colors);
next.add(color);
apply.test(next);
reopen.run();
}, reopen)));
}
- inventory.setItem(23, new SWItem(Material.WHITE_DYE, msg("PARTICLE_COLOR_RANDOM", player), clickType -> {
+ inventory.setItem(RESET, new SWItem(Material.WHITE_DYE, msg("PARTICLE_COLOR_RANDOM", player), clickType -> {
apply.test(List.of());
reopen.run();
}));
- SlotEditor.fill(inventory, 0, 27);
+ BottomBar.fillUnused(inventory, SIZE, false);
+ SlotEditor.fill(inventory, 0, SIZE);
inventory.open();
}