forked from SteamWar/SteamWar
Clicking a step past the minimum or maximum, or setting an unchanged value, now does nothing instead of showing the budget message, and changes that lower a slot's particles are allowed even over budget. Entries show their particles per tick in the slot editor list, and the entry editor's paper shows the edited entry's share.
184 lines
8.9 KiB
Java
184 lines
8.9 KiB
Java
/*
|
|
* 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 <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
package de.steamwar.lobby.particle.gui;
|
|
|
|
import de.steamwar.inventory.SWAnvilInv;
|
|
import de.steamwar.inventory.SWInventory;
|
|
import de.steamwar.inventory.SWItem;
|
|
import de.steamwar.lobby.LobbySystem;
|
|
import lombok.experimental.UtilityClass;
|
|
import org.bukkit.Bukkit;
|
|
import org.bukkit.DyeColor;
|
|
import org.bukkit.Material;
|
|
import org.bukkit.entity.Player;
|
|
|
|
import java.awt.Color;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.function.Predicate;
|
|
import java.util.function.Supplier;
|
|
|
|
import static de.steamwar.lobby.particle.gui.ParticleGuiText.*;
|
|
|
|
// Screens for settings that need more than a click: numbers and colour lists
|
|
@UtilityClass
|
|
class ValueEditors {
|
|
|
|
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<Double> current, double step, double min, double max, double defaultValue, boolean integer,
|
|
Predicate<Double> apply, Runnable back) {
|
|
double value = current.get();
|
|
Runnable reopen = () -> editNumber(player, title, current, step, min, max, defaultValue, integer, apply, back);
|
|
|
|
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));
|
|
inventory.setItem(16, stepItem(player, Material.LIME_CONCRETE, 10 * step, step, value, min, max, integer, apply, reopen));
|
|
|
|
List<String> lore = List.of(msg("PARTICLE_NUMBER_RANGE", player, number(min), number(max)), "", msg("PARTICLE_NUMBER_TYPE", player));
|
|
inventory.setItem(13, new SWItem(Material.PAPER, "§e" + number(value), lore, false, clickType -> {
|
|
SWAnvilInv anvil = new SWAnvilInv(player, strip(title), number(value));
|
|
anvil.setItem(Material.PAPER);
|
|
anvil.setCallback(input -> Bukkit.getScheduler().runTask(LobbySystem.getInstance(), () -> {
|
|
try {
|
|
double typed = Double.parseDouble(input.trim().replace(',', '.'));
|
|
apply.test(integer ? Math.round(typed) : typed);
|
|
} catch (NumberFormatException e) {
|
|
// Keep the old value
|
|
}
|
|
reopen.run();
|
|
}));
|
|
anvil.open();
|
|
}));
|
|
|
|
inventory.setItem(BACK, new SWItem(Material.ARROW, msg("PARTICLE_BACK", player), clickType -> back.run()));
|
|
inventory.setItem(RESET, new SWItem(Material.STRUCTURE_VOID, msg("PARTICLE_VALUE_RESET", player, number(defaultValue)), clickType -> {
|
|
apply.test(defaultValue);
|
|
reopen.run();
|
|
}));
|
|
BottomBar.fillUnused(inventory, SIZE, false);
|
|
SlotEditor.fill(inventory, 0, SIZE);
|
|
inventory.open();
|
|
}
|
|
|
|
private SWItem stepItem(Player player, Material material, double delta, double step, double value, double min, double max, boolean integer, Predicate<Double> apply, Runnable reopen) {
|
|
String label = (delta < 0 ? "§c-" : "§a+") + number(Math.abs(delta));
|
|
// Snap to the step grid and drop floating point noise, so repeated steps do not drift
|
|
double unclamped = integer ? Math.round(value + delta) : Math.round(Math.round((value + delta) / step) * step * 1e6) / 1e6;
|
|
double next = Math.max(min, Math.min(max, unclamped));
|
|
String limit = next == max && unclamped >= max ? "PARTICLE_NUMBER_AT_MAX" : next == min && unclamped <= min ? "PARTICLE_NUMBER_AT_MIN" : "PARTICLE_NUMBER_RESULT";
|
|
return new SWItem(material, label, List.of(msg(limit, player, number(next))), false, clickType -> {
|
|
// Stepping past the minimum or maximum changes nothing, so it does nothing
|
|
if (next == value) return;
|
|
apply.test(next);
|
|
reopen.run();
|
|
});
|
|
}
|
|
|
|
// Shows the colours in order; a click removes one, the buttons below add one or switch to random colours
|
|
void editColors(Player player, Supplier<List<Color>> current, int maxColors, Predicate<List<Color>> apply, Runnable back) {
|
|
List<Color> colors = current.get();
|
|
Runnable reopen = () -> editColors(player, current, maxColors, apply, back);
|
|
|
|
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);
|
|
inventory.setItem(i, new SWItem(dye(color), net.md_5.bungee.api.ChatColor.of(color) + "█ #" + String.format("%06X", color.getRGB() & 0xFFFFFF),
|
|
List.of(msg("PARTICLE_COLOR_REMOVE", player)), false, clickType -> {
|
|
List<Color> next = new ArrayList<>(colors);
|
|
next.remove(position);
|
|
apply.test(next);
|
|
reopen.run();
|
|
}));
|
|
}
|
|
if (colors.isEmpty()) {
|
|
inventory.setItem(4, new SWItem(Material.WHITE_DYE, msg("PARTICLE_VALUE_RANDOM", player), clickType -> {
|
|
}));
|
|
}
|
|
|
|
inventory.setItem(BACK, new SWItem(Material.ARROW, msg("PARTICLE_BACK", player), clickType -> back.run()));
|
|
if (colors.size() < maxColors) {
|
|
inventory.setItem(ADD, new SWItem(Material.NETHER_STAR, msg("PARTICLE_COLOR_ADD", player), clickType -> Pickers.color(player, color -> {
|
|
List<Color> next = new ArrayList<>(colors);
|
|
next.add(color);
|
|
apply.test(next);
|
|
reopen.run();
|
|
}, reopen)));
|
|
// Same position as typing a value on the number screen
|
|
inventory.setItem(13, new SWItem(Material.NAME_TAG, msg("PARTICLE_COLOR_HEX", player), List.of(msg("PARTICLE_COLOR_HEX_EXAMPLE", player)), false, clickType -> {
|
|
SWAnvilInv anvil = new SWAnvilInv(player, msg("PARTICLE_COLOR_HEX_TITLE", player), "#");
|
|
anvil.setItem(Material.NAME_TAG);
|
|
anvil.setCallback(input -> Bukkit.getScheduler().runTask(LobbySystem.getInstance(), () -> {
|
|
Color color = parseHex(input);
|
|
if (color == null) {
|
|
LobbySystem.getMessage().send("PARTICLE_COLOR_HEX_INVALID", player);
|
|
} else {
|
|
List<Color> next = new ArrayList<>(colors);
|
|
next.add(color);
|
|
apply.test(next);
|
|
}
|
|
reopen.run();
|
|
}));
|
|
anvil.open();
|
|
}));
|
|
}
|
|
inventory.setItem(RESET, new SWItem(Material.WHITE_DYE, msg("PARTICLE_COLOR_RANDOM", player), clickType -> {
|
|
apply.test(List.of());
|
|
reopen.run();
|
|
}));
|
|
BottomBar.fillUnused(inventory, SIZE, false);
|
|
SlotEditor.fill(inventory, 0, SIZE);
|
|
inventory.open();
|
|
}
|
|
|
|
// Accepts #RRGGBB and #RGB, with or without the #; null for anything else
|
|
Color parseHex(String input) {
|
|
String hex = input.trim();
|
|
if (hex.startsWith("#")) hex = hex.substring(1);
|
|
if (hex.length() == 3) {
|
|
hex = "" + hex.charAt(0) + hex.charAt(0) + hex.charAt(1) + hex.charAt(1) + hex.charAt(2) + hex.charAt(2);
|
|
}
|
|
if (!hex.matches("[0-9a-fA-F]{6}")) {
|
|
return null;
|
|
}
|
|
return new Color(Integer.parseInt(hex, 16));
|
|
}
|
|
|
|
private Material dye(Color color) {
|
|
for (DyeColor dye : DyeColor.values()) {
|
|
if ((dye.getColor().asRGB() & 0xFFFFFF) == (color.getRGB() & 0xFFFFFF)) {
|
|
return Material.valueOf(dye.name() + "_DYE");
|
|
}
|
|
}
|
|
return Material.WHITE_DYE;
|
|
}
|
|
|
|
private String strip(String text) {
|
|
return text.replaceAll("§.", "");
|
|
}
|
|
}
|