/*
* 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 de.steamwar.lobby.LobbySystem;
import de.steamwar.lobby.particle.UnlockContext;
import de.steamwar.lobby.particle.WingDesign;
import de.steamwar.lobby.particle.design.*;
import de.steamwar.lobby.particle.slot.ParticleEntry;
import de.steamwar.lobby.particle.slot.ParticleSlot;
import de.steamwar.lobby.particle.slot.ParticleSlots;
import de.steamwar.lobby.util.LobbyPlayer;
import de.steamwar.sql.SteamwarUser;
import lombok.experimental.UtilityClass;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import static de.steamwar.lobby.particle.design.EntryParameters.*;
import static de.steamwar.lobby.particle.gui.ParticleGuiText.*;
// Settings of one entry in labelled rows; a click on a setting toggles it or opens its own screen
@UtilityClass
class EntryEditor {
private final int ROW_DESIGN = 0;
private final int ROW_PARTICLE = 1;
private final int ROW_SHAPE = 2;
private final int ROW_TIMING = 3;
private final int ROW_CONDITIONS = 4;
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 int MAX_CYCLED_OPTIONS = 5;
private final Map, Material> ICONS = Map.ofEntries(
Map.entry(COUNT, Material.GLOWSTONE_DUST),
Map.entry(SPEED, Material.FEATHER),
Map.entry(SPREAD_X, Material.GUNPOWDER),
Map.entry(SPREAD_Y, Material.GUNPOWDER),
Map.entry(SPREAD_Z, Material.GUNPOWDER),
Map.entry(OFFSET_X, Material.SPECTRAL_ARROW),
Map.entry(OFFSET_Y, Material.SPECTRAL_ARROW),
Map.entry(OFFSET_Z, Material.SPECTRAL_ARROW),
Map.entry(INTERVAL, Material.CLOCK),
Map.entry(TICK_MODE, Material.REPEATER),
Map.entry(MOVEMENT, Material.LEATHER_BOOTS),
Map.entry(GLIDING, Material.ELYTRA),
Map.entry(GROUND, Material.GRASS_BLOCK),
Map.entry(SNEAK, Material.LEATHER_LEGGINGS),
Map.entry(VISIBILITY, Material.SPYGLASS),
Map.entry(Designs.CIRCLE_RADIUS, Material.COMPASS),
Map.entry(Designs.CIRCLE_SPEED, Material.RECOVERY_COMPASS),
Map.entry(Designs.CIRCLE_PHASE, Material.TARGET),
Map.entry(Designs.CIRCLE_RISE, Material.LADDER),
Map.entry(Designs.CIRCLE_RISE_SPEED, Material.RABBIT_FOOT),
Map.entry(Designs.WING_IMAGE, Material.PHANTOM_MEMBRANE),
Map.entry(Designs.WING_SIZE, Material.SCAFFOLDING)
);
private record Context(Player player, SteamwarUser user, int index, int entryIndex, ParticleSlot slot, ParticleEntry entry) {
void reopen() {
open(player, index, entryIndex);
}
}
void open(Player player, int index, int entryIndex) {
LobbyPlayer lobbyPlayer = LobbyPlayer.getLobbyPlayer(player);
ParticleSlot slot = lobbyPlayer.getSlot(index);
SteamwarUser user = SteamwarUser.get(player.getUniqueId());
if (slot == null || entryIndex >= slot.getEntries().size() || !ParticleSlots.requirement(index).test(new UnlockContext(user))) {
SlotEditor.open(player, index);
return;
}
Context context = new Context(player, user, index, entryIndex, slot, slot.getEntries().get(entryIndex));
ParticleEntry entry = context.entry();
ParticleDesign design = entry.getDesign();
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);
header(inventory, ROW_TIMING, Material.DAYLIGHT_DETECTOR, "PARTICLE_SECTION_TIMING", player);
header(inventory, ROW_CONDITIONS, Material.OBSERVER, "PARTICLE_SECTION_CONDITIONS", player);
inventory.setItem(ROW_DESIGN * 9 + 1, new SWItem(design.getIcon(), designName(design, player), List.of(msg("PARTICLE_VALUE_CLICK", player)), false,
clickType -> Pickers.design(player, design, picked -> changeDesign(context, picked), context::reopen)));
if (design.usesEntryParticle()) {
inventory.setItem(ROW_DESIGN * 9 + 2, parameterItem(context, PARTICLE));
ParticleType type = entry.getValues().get(PARTICLE);
if (type.usesColors()) {
inventory.setItem(ROW_DESIGN * 9 + 3, parameterItem(context, COLORS));
} else if (type.usesBlock()) {
inventory.setItem(ROW_DESIGN * 9 + 3, parameterItem(context, BLOCK));
}
row(inventory, context, ROW_PARTICLE, List.of(COUNT, SPEED, SPREAD_X, SPREAD_Y, SPREAD_Z));
} else {
inventory.setItem(ROW_PARTICLE * 9 + 1, new SWItem(Material.STRUCTURE_VOID, msg("PARTICLE_FIXED_LOOK", player), clickType -> {
}));
}
row(inventory, context, ROW_SHAPE, design.getParameters());
row(inventory, context, ROW_TIMING, List.of(TICK_MODE, INTERVAL, OFFSET_X, OFFSET_Y, OFFSET_Z));
row(inventory, context, ROW_CONDITIONS, List.of(MOVEMENT, GLIDING, GROUND, SNEAK, VISIBILITY));
inventory.setItem(BACK, new SWItem(Material.ARROW, msg("PARTICLE_BACK", player), clickType -> SlotEditor.open(player, index)));
inventory.setItem(COST, SlotEditor.costItem(player, user, slot));
inventory.setItem(DELETE, new SWItem(Material.LAVA_BUCKET, msg("PARTICLE_ENTRY_DELETE", player), List.of(msg("PARTICLE_SHIFT_TO_DELETE", player)), false, clickType -> {
if (!clickType.isShiftClick()) return;
slot.getEntries().remove(entryIndex);
SlotEditor.save(player, index, slot);
SlotEditor.open(player, index);
}));
BottomBar.fillUnused(inventory, SIZE, false);
SlotEditor.fill(inventory, 0, SIZE);
inventory.open();
}
// Glowing so the row labels stand apart from the settings next to them
private void header(SWInventory inventory, int row, Material icon, String key, Player player) {
inventory.setItem(row * 9, new SWItem(icon, msg(key, player), List.of(), true, clickType -> {
}));
}
private void row(SWInventory inventory, Context context, int row, List> parameters) {
for (int i = 0; i < parameters.size() && i < 8; i++) {
inventory.setItem(row * 9 + 1 + i, parameterItem(context, parameters.get(i)));
}
}
@SuppressWarnings("unchecked")
private SWItem parameterItem(Context context, Parameter> parameter) {
Player player = context.player();
Object value = context.entry().getValues().get(parameter);
List lore = new ArrayList<>();
if (parameter instanceof ChoiceParameter> choice && isCycled(choice)) {
lore.addAll(optionLines((ChoiceParameter