Add GUI framework for class selection in CTF
Introduced a GUI system to facilitate class selection in the CTF game. This includes an abstract GUIInventory, GUIListener for event handling, and a ClassSelectionGUI for selecting classes. Also, added the "selectclass" command and integrated the GUI with game mechanics for improved player interaction.
This commit is contained in:
parent
6a9721d0e7
commit
73c11ab1ca
|
|
@ -10,6 +10,7 @@ import com.alttd.ctf.events.OnSnowballHit;
|
||||||
import com.alttd.ctf.flag.Flag;
|
import com.alttd.ctf.flag.Flag;
|
||||||
import com.alttd.ctf.flag.FlagTryCaptureEvent;
|
import com.alttd.ctf.flag.FlagTryCaptureEvent;
|
||||||
import com.alttd.ctf.game.GameManager;
|
import com.alttd.ctf.game.GameManager;
|
||||||
|
import com.alttd.ctf.gui.GUIListener;
|
||||||
import com.alttd.ctf.json_config.JacksonConfig;
|
import com.alttd.ctf.json_config.JacksonConfig;
|
||||||
import com.alttd.ctf.json_config.JsonConfigManager;
|
import com.alttd.ctf.json_config.JsonConfigManager;
|
||||||
import com.alttd.ctf.team.Team;
|
import com.alttd.ctf.team.Team;
|
||||||
|
|
@ -69,6 +70,7 @@ public class Main extends JavaPlugin {
|
||||||
pluginManager.registerEvents(new FlagTryCaptureEvent(flag), this);
|
pluginManager.registerEvents(new FlagTryCaptureEvent(flag), this);
|
||||||
pluginManager.registerEvents(new OnPlayerDeath(gameManager), this);
|
pluginManager.registerEvents(new OnPlayerDeath(gameManager), this);
|
||||||
pluginManager.registerEvents(new OnPlayerJoin(gameManager, flag), this);
|
pluginManager.registerEvents(new OnPlayerJoin(gameManager, flag), this);
|
||||||
|
pluginManager.registerEvents(new GUIListener(), this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void registerTeams() {
|
private void registerTeams() {
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,7 @@
|
||||||
package com.alttd.ctf.commands;
|
package com.alttd.ctf.commands;
|
||||||
|
|
||||||
import com.alttd.ctf.Main;
|
import com.alttd.ctf.Main;
|
||||||
import com.alttd.ctf.commands.subcommands.ChangeTeam;
|
import com.alttd.ctf.commands.subcommands.*;
|
||||||
import com.alttd.ctf.commands.subcommands.CreateTeam;
|
|
||||||
import com.alttd.ctf.commands.subcommands.Reload;
|
|
||||||
import com.alttd.ctf.commands.subcommands.Start;
|
|
||||||
import com.alttd.ctf.config.Messages;
|
import com.alttd.ctf.config.Messages;
|
||||||
import com.alttd.ctf.flag.Flag;
|
import com.alttd.ctf.flag.Flag;
|
||||||
import com.alttd.ctf.game.GameManager;
|
import com.alttd.ctf.game.GameManager;
|
||||||
|
|
@ -39,6 +36,7 @@ public class CommandManager implements CommandExecutor, TabExecutor {
|
||||||
new ChangeTeam(gameManager),
|
new ChangeTeam(gameManager),
|
||||||
new Start(gameManager, flag),
|
new Start(gameManager, flag),
|
||||||
new CreateTeam(main, gameManager),
|
new CreateTeam(main, gameManager),
|
||||||
|
new SelectClass(gameManager),
|
||||||
new Reload(main)
|
new Reload(main)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
package com.alttd.ctf.commands.subcommands;
|
||||||
|
|
||||||
|
import com.alttd.ctf.commands.SubCommand;
|
||||||
|
import com.alttd.ctf.config.Messages;
|
||||||
|
import com.alttd.ctf.game.GameManager;
|
||||||
|
import com.alttd.ctf.game_class.GameClass;
|
||||||
|
import com.alttd.ctf.game_class.creation.FighterCreator;
|
||||||
|
import com.alttd.ctf.gui.ClassSelectionGUI;
|
||||||
|
import com.alttd.ctf.team.TeamPlayer;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public class SelectClass extends SubCommand {
|
||||||
|
|
||||||
|
private final GameManager gameManager;
|
||||||
|
private final HashMap<Integer, List<GameClass>> gameClasses;
|
||||||
|
|
||||||
|
public SelectClass(GameManager gameManager) {
|
||||||
|
this.gameManager = gameManager;
|
||||||
|
this.gameClasses = new HashMap<>();
|
||||||
|
gameManager.getTeams().forEach(team -> {
|
||||||
|
gameClasses.computeIfAbsent(team.getId(), teamId -> new ArrayList<>()).add(FighterCreator.createFighter(team.getColor()));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int onCommand(CommandSender commandSender, String[] args) {
|
||||||
|
if (!(commandSender instanceof Player player)) {
|
||||||
|
commandSender.sendRichMessage(Messages.GENERIC.PLAYER_ONLY);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (gameManager.getGamePhase().isEmpty()) {
|
||||||
|
commandSender.sendRichMessage("<red>CTF has to be running to select a class.</red>");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
Optional<TeamPlayer> optionalTeamPlayer = gameManager.getTeamPlayer(player.getUniqueId());
|
||||||
|
if (optionalTeamPlayer.isEmpty()) {
|
||||||
|
commandSender.sendRichMessage("<red>You have to be in a CTF team to select a class.</red>");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
TeamPlayer teamPlayer = optionalTeamPlayer.get();
|
||||||
|
if (teamPlayer.getTeam().getSpawnLocation().distance(player.getLocation()) > 15) {
|
||||||
|
commandSender.sendRichMessage("<red>You have to be near your spawn to change classes.</red>");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
new ClassSelectionGUI(gameClasses.get(teamPlayer.getTeam().getId()), teamPlayer)
|
||||||
|
.open(player);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "selectclass";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getTabComplete(CommandSender commandSender, String[] args) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getHelpMessage() {
|
||||||
|
return Messages.HELP.SELECT_CLASS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -26,6 +26,7 @@ public class Messages extends AbstractConfig {
|
||||||
public static String CHANGE_TEAM = "<green>Change a players team: <gold>/ctf changeteam <player> <team></gold></green>";
|
public static String CHANGE_TEAM = "<green>Change a players team: <gold>/ctf changeteam <player> <team></gold></green>";
|
||||||
public static String CREATE_TEAM = "<green>Create a team: <gold>/ctf createteam <team_name> <hex_color></gold></green>";
|
public static String CREATE_TEAM = "<green>Create a team: <gold>/ctf createteam <team_name> <hex_color></gold></green>";
|
||||||
public static String START = "<green>Start a new game: <gold>/ctf start <time_in_minutes></gold></green>";
|
public static String START = "<green>Start a new game: <gold>/ctf start <time_in_minutes></gold></green>";
|
||||||
|
public static String SELECT_CLASS = "<green>Open class selection: <gold>/ctf selectclass</gold></green>";
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
private static void load() {
|
private static void load() {
|
||||||
|
|
@ -35,6 +36,7 @@ public class Messages extends AbstractConfig {
|
||||||
CHANGE_TEAM = config.getString(prefix, "change-team", CHANGE_TEAM);
|
CHANGE_TEAM = config.getString(prefix, "change-team", CHANGE_TEAM);
|
||||||
CREATE_TEAM = config.getString(prefix, "create-team", CREATE_TEAM);
|
CREATE_TEAM = config.getString(prefix, "create-team", CREATE_TEAM);
|
||||||
START = config.getString(prefix, "start", START);
|
START = config.getString(prefix, "start", START);
|
||||||
|
SELECT_CLASS = config.getString(prefix, "select-class", SELECT_CLASS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ import com.alttd.ctf.team.TeamColor;
|
||||||
import com.alttd.ctf.team.TeamPlayer;
|
import com.alttd.ctf.team.TeamPlayer;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Color;
|
import org.bukkit.Color;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
|
|
@ -13,6 +15,7 @@ import org.bukkit.entity.Player;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.bukkit.inventory.meta.ItemMeta;
|
import org.bukkit.inventory.meta.ItemMeta;
|
||||||
import org.bukkit.inventory.meta.LeatherArmorMeta;
|
import org.bukkit.inventory.meta.LeatherArmorMeta;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
|
@ -21,7 +24,10 @@ public abstract class GameClass {
|
||||||
|
|
||||||
private final List<Material> armor;
|
private final List<Material> armor;
|
||||||
private final List<ItemStack> tools;
|
private final List<ItemStack> tools;
|
||||||
|
@Getter
|
||||||
private final ItemStack displayItem;
|
private final ItemStack displayItem;
|
||||||
|
@Getter
|
||||||
|
private final Component className;
|
||||||
private final double health;
|
private final double health;
|
||||||
//Delay in ticks between throws (prevents auto clickers)
|
//Delay in ticks between throws (prevents auto clickers)
|
||||||
@Getter
|
@Getter
|
||||||
|
|
@ -30,7 +36,7 @@ public abstract class GameClass {
|
||||||
@Getter
|
@Getter
|
||||||
private final int damage;
|
private final int damage;
|
||||||
|
|
||||||
protected GameClass(List<Material> armor, List<ItemStack> tools, ItemStack displayItem, double health, int throwTickSpeed, int damage) {
|
protected GameClass(@NotNull List<Material> armor, @NotNull List<ItemStack> tools, @NotNull ItemStack displayItem, double health, int throwTickSpeed, int damage) {
|
||||||
if (armor.size() != 4) {
|
if (armor.size() != 4) {
|
||||||
throw new IllegalArgumentException("Armor has to have 4 entries");
|
throw new IllegalArgumentException("Armor has to have 4 entries");
|
||||||
}
|
}
|
||||||
|
|
@ -40,6 +46,11 @@ public abstract class GameClass {
|
||||||
this.health = health;
|
this.health = health;
|
||||||
this.throwTickSpeed = throwTickSpeed;
|
this.throwTickSpeed = throwTickSpeed;
|
||||||
this.damage = damage;
|
this.damage = damage;
|
||||||
|
ItemMeta itemMeta = displayItem.getItemMeta();
|
||||||
|
if (itemMeta == null) {
|
||||||
|
throw new IllegalArgumentException("Display item has no item meta");
|
||||||
|
}
|
||||||
|
this.className = itemMeta.displayName();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void apply(TeamPlayer teamPlayer) {
|
public void apply(TeamPlayer teamPlayer) {
|
||||||
|
|
@ -64,6 +75,8 @@ public abstract class GameClass {
|
||||||
|
|
||||||
player.updateInventory();
|
player.updateInventory();
|
||||||
teamPlayer.setGameClass(this);
|
teamPlayer.setGameClass(this);
|
||||||
|
player.sendRichMessage("You selected the <class_name> class", Placeholder.component("class_name", className));
|
||||||
|
player.teleportAsync(teamPlayer.getTeam().getSpawnLocation());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setArmor(Player player, int r, int g, int b) {
|
private void setArmor(Player player, int r, int g, int b) {
|
||||||
|
|
@ -78,10 +91,6 @@ public abstract class GameClass {
|
||||||
}).toArray(ItemStack[]::new));
|
}).toArray(ItemStack[]::new));
|
||||||
}
|
}
|
||||||
|
|
||||||
ItemStack getDisplayItem() {
|
|
||||||
return displayItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setArmor(Player player, TeamPlayer teamPlayer) {
|
public void setArmor(Player player, TeamPlayer teamPlayer) {
|
||||||
TeamColor color = teamPlayer.getTeam().getColor();
|
TeamColor color = teamPlayer.getTeam().getColor();
|
||||||
setArmor(player, color.r(), color.g(), color.b());
|
setArmor(player, color.r(), color.g(), color.b());
|
||||||
|
|
|
||||||
29
src/main/java/com/alttd/ctf/gui/ClassSelectionGUI.java
Normal file
29
src/main/java/com/alttd/ctf/gui/ClassSelectionGUI.java
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
package com.alttd.ctf.gui;
|
||||||
|
|
||||||
|
import com.alttd.ctf.game_class.GameClass;
|
||||||
|
import com.alttd.ctf.team.TeamPlayer;
|
||||||
|
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||||
|
import org.bukkit.event.inventory.InventoryCloseEvent;
|
||||||
|
import org.bukkit.event.inventory.InventoryType;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ClassSelectionGUI extends GUIInventory {
|
||||||
|
|
||||||
|
public ClassSelectionGUI(@NotNull List<GameClass> gameClasses, @NotNull TeamPlayer teamPlayer) {
|
||||||
|
super(InventoryType.CHEST, teamPlayer.getTeam().getName().append(MiniMessage.miniMessage().deserialize(" - class selection")));
|
||||||
|
createClassSelection(gameClasses, teamPlayer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createClassSelection(@NotNull List<GameClass> gameClasses, @NotNull TeamPlayer teamPlayer) {
|
||||||
|
int pos = (9 + (9 - gameClasses.size()) / 2);
|
||||||
|
for (GameClass gameClass : gameClasses) {
|
||||||
|
setItem(pos++, gameClass.getDisplayItem(), player -> {
|
||||||
|
gameClass.apply(teamPlayer);
|
||||||
|
player.closeInventory(InventoryCloseEvent.Reason.PLUGIN);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
19
src/main/java/com/alttd/ctf/gui/GUI.java
Normal file
19
src/main/java/com/alttd/ctf/gui/GUI.java
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
package com.alttd.ctf.gui;
|
||||||
|
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.inventory.Inventory;
|
||||||
|
import org.bukkit.inventory.Merchant;
|
||||||
|
import org.bukkit.inventory.MerchantInventory;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public interface GUI {
|
||||||
|
HashMap<UUID, GUI> GUIByUUID = new HashMap<>();
|
||||||
|
|
||||||
|
void open(Player player);
|
||||||
|
|
||||||
|
GUIAction getGuiAction(int slot);
|
||||||
|
|
||||||
|
Inventory getInventory();
|
||||||
|
}
|
||||||
7
src/main/java/com/alttd/ctf/gui/GUIAction.java
Normal file
7
src/main/java/com/alttd/ctf/gui/GUIAction.java
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
package com.alttd.ctf.gui;
|
||||||
|
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
|
public interface GUIAction {
|
||||||
|
void click(Player player);
|
||||||
|
}
|
||||||
46
src/main/java/com/alttd/ctf/gui/GUIInventory.java
Normal file
46
src/main/java/com/alttd/ctf/gui/GUIInventory.java
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
package com.alttd.ctf.gui;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.inventory.InventoryType;
|
||||||
|
import org.bukkit.inventory.Inventory;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.bukkit.inventory.Merchant;
|
||||||
|
import org.bukkit.inventory.MerchantInventory;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
public abstract class GUIInventory implements GUI {
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
protected final Inventory inventory;
|
||||||
|
protected final HashMap<Integer, GUIAction> guiActions;
|
||||||
|
|
||||||
|
public GUIInventory(InventoryType type, Component name) {
|
||||||
|
inventory = Bukkit.createInventory(null, type, name);
|
||||||
|
guiActions = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setItem(int slot, ItemStack stack, GUIAction action) {
|
||||||
|
this.inventory.setItem(slot, stack);
|
||||||
|
if (action != null) {
|
||||||
|
guiActions.put(slot, action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setItem(int slot, ItemStack stack) {
|
||||||
|
setItem(slot, stack, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void open(Player player) {
|
||||||
|
player.openInventory(inventory);
|
||||||
|
GUIByUUID.put(player.getUniqueId(), this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GUIAction getGuiAction(int slot) {
|
||||||
|
return guiActions.get(slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
60
src/main/java/com/alttd/ctf/gui/GUIListener.java
Normal file
60
src/main/java/com/alttd/ctf/gui/GUIListener.java
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
package com.alttd.ctf.gui;
|
||||||
|
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||||
|
import org.bukkit.event.inventory.InventoryCloseEvent;
|
||||||
|
import org.bukkit.event.inventory.InventoryType;
|
||||||
|
import org.bukkit.event.player.PlayerQuitEvent;
|
||||||
|
import org.bukkit.inventory.Inventory;
|
||||||
|
|
||||||
|
public class GUIListener implements Listener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles clicking inside a gui
|
||||||
|
* @param event gui click event
|
||||||
|
*/
|
||||||
|
@EventHandler
|
||||||
|
public void onClick(InventoryClickEvent event){
|
||||||
|
if (!(event.getWhoClicked() instanceof Player player)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GUI gui = GUI.GUIByUUID.get(player.getUniqueId());
|
||||||
|
if (gui == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (gui.getInventory() != null) {
|
||||||
|
if (!gui.getInventory().equals(event.getInventory())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
event.setCancelled(true);
|
||||||
|
|
||||||
|
Inventory clickedInventory = event.getClickedInventory();
|
||||||
|
if (clickedInventory == null || clickedInventory.getType().equals(InventoryType.PLAYER)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GUIAction action = gui.getGuiAction(event.getSlot());
|
||||||
|
|
||||||
|
if (action != null) {
|
||||||
|
action.click(player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onClose(InventoryCloseEvent event) {
|
||||||
|
GUI.GUIByUUID.remove(event.getPlayer().getUniqueId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onQuit(PlayerQuitEvent event){
|
||||||
|
GUI.GUIByUUID.remove(event.getPlayer().getUniqueId());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
#Fri Feb 07 22:07:29 CET 2025
|
#Fri Feb 07 23:09:43 CET 2025
|
||||||
buildNumber=5
|
buildNumber=8
|
||||||
version=0.1
|
version=0.1
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user