From 726a4b2d3de830e36e2baa663aae005aee074ef2 Mon Sep 17 00:00:00 2001 From: applenick Date: Sat, 28 Aug 2021 11:40:59 -0700 Subject: [PATCH 1/3] Introduce settings menu and redesigned match stats menu Signed-off-by: applenick --- core/pom.xml | 1 + core/src/main/java/tc/oc/pgm/PGMPlugin.java | 10 + core/src/main/java/tc/oc/pgm/api/Modules.java | 2 + core/src/main/java/tc/oc/pgm/api/PGM.java | 3 + .../tc/oc/pgm/api/setting/SettingKey.java | 76 +++- .../tc/oc/pgm/api/setting/SettingValue.java | 66 ++-- .../tc/oc/pgm/command/SettingCommand.java | 12 +- .../java/tc/oc/pgm/menu/InventoryMenu.java | 97 ++++++ .../main/java/tc/oc/pgm/menu/MenuItem.java | 43 ++- .../tc/oc/pgm/menu/PagedInventoryMenu.java | 138 ++++++++ .../oc/pgm/modules/PlayerTimeMatchModule.java | 47 +++ .../ObserverToolsInventoryMenuItem.java | 45 --- .../observers/ObserverToolsMatchModule.java | 68 ++-- .../oc/pgm/observers/ObserverToolsMenu.java | 36 ++ .../oc/pgm/observers/tools/FlySpeedTool.java | 10 +- .../oc/pgm/observers/tools/GamemodeTool.java | 14 +- .../pgm/observers/tools/NightVisionTool.java | 20 +- .../oc/pgm/observers/tools/SettingsTool.java | 42 +++ .../pgm/observers/tools/VisibilityTool.java | 64 ---- .../java/tc/oc/pgm/settings/SettingsMenu.java | 139 ++++++++ .../tc/oc/pgm/stats/StatsMatchModule.java | 62 ++-- .../main/java/tc/oc/pgm/stats/TeamStats.java | 72 ++++ .../tc/oc/pgm/stats/menu/StatsMainMenu.java | 106 ++++++ .../tc/oc/pgm/stats/menu/TeamStatsMenu.java | 94 +++++ .../items/PlayerStatsMenuItem.java} | 13 +- .../items/TeamStatsMenuItem.java} | 97 +++--- .../items/VerboseStatsMenuItem.java} | 20 +- pom.xml | 8 + util/src/main/i18n/templates/misc.properties | 4 + util/src/main/i18n/templates/ui.properties | 89 +++++ .../tc/oc/pgm/util/menu/InventoryMenu.java | 329 ------------------ .../oc/pgm/util/menu/InventoryMenuUtils.java | 65 ---- .../menu/pattern/DoubleRowMenuArranger.java | 41 --- .../menu/pattern/IdentityMenuArranger.java | 31 -- .../pgm/util/menu/pattern/MenuArranger.java | 23 -- .../menu/pattern/SingleRowMenuArranger.java | 35 -- 36 files changed, 1162 insertions(+), 860 deletions(-) create mode 100644 core/src/main/java/tc/oc/pgm/menu/InventoryMenu.java rename util/src/main/java/tc/oc/pgm/util/menu/InventoryMenuItem.java => core/src/main/java/tc/oc/pgm/menu/MenuItem.java (53%) create mode 100644 core/src/main/java/tc/oc/pgm/menu/PagedInventoryMenu.java create mode 100644 core/src/main/java/tc/oc/pgm/modules/PlayerTimeMatchModule.java delete mode 100644 core/src/main/java/tc/oc/pgm/observers/ObserverToolsInventoryMenuItem.java create mode 100644 core/src/main/java/tc/oc/pgm/observers/ObserverToolsMenu.java create mode 100644 core/src/main/java/tc/oc/pgm/observers/tools/SettingsTool.java delete mode 100644 core/src/main/java/tc/oc/pgm/observers/tools/VisibilityTool.java create mode 100644 core/src/main/java/tc/oc/pgm/settings/SettingsMenu.java create mode 100644 core/src/main/java/tc/oc/pgm/stats/TeamStats.java create mode 100644 core/src/main/java/tc/oc/pgm/stats/menu/StatsMainMenu.java create mode 100644 core/src/main/java/tc/oc/pgm/stats/menu/TeamStatsMenu.java rename core/src/main/java/tc/oc/pgm/stats/{PlayerStatsInventoryMenuItem.java => menu/items/PlayerStatsMenuItem.java} (94%) rename core/src/main/java/tc/oc/pgm/stats/{TeamStatsInventoryMenuItem.java => menu/items/TeamStatsMenuItem.java} (57%) rename core/src/main/java/tc/oc/pgm/stats/{VerboseStatsInventoryMenuItem.java => menu/items/VerboseStatsMenuItem.java} (58%) delete mode 100644 util/src/main/java/tc/oc/pgm/util/menu/InventoryMenu.java delete mode 100644 util/src/main/java/tc/oc/pgm/util/menu/InventoryMenuUtils.java delete mode 100644 util/src/main/java/tc/oc/pgm/util/menu/pattern/DoubleRowMenuArranger.java delete mode 100644 util/src/main/java/tc/oc/pgm/util/menu/pattern/IdentityMenuArranger.java delete mode 100644 util/src/main/java/tc/oc/pgm/util/menu/pattern/MenuArranger.java delete mode 100644 util/src/main/java/tc/oc/pgm/util/menu/pattern/SingleRowMenuArranger.java diff --git a/core/pom.xml b/core/pom.xml index 81e4859bca..f436f72fa0 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -51,6 +51,7 @@ fr.mrmicky:*:* org.eclipse.jgit:org.eclipse.jgit:* org.slf4j:slf4j-api:* + fr.minuskube.inv:smart-invs diff --git a/core/src/main/java/tc/oc/pgm/PGMPlugin.java b/core/src/main/java/tc/oc/pgm/PGMPlugin.java index 6c2300bf45..6f27b0a109 100644 --- a/core/src/main/java/tc/oc/pgm/PGMPlugin.java +++ b/core/src/main/java/tc/oc/pgm/PGMPlugin.java @@ -1,6 +1,7 @@ package tc.oc.pgm; import com.google.common.collect.Lists; +import fr.minuskube.inv.InventoryManager; import java.io.File; import java.sql.SQLException; import java.util.ArrayList; @@ -92,6 +93,7 @@ public class PGMPlugin extends JavaPlugin implements PGM, Listener { private ScheduledExecutorService executorService; private ScheduledExecutorService asyncExecutorService; private VanishManager vanishManager; + private InventoryManager inventoryManager; public PGMPlugin() { super(); @@ -211,6 +213,9 @@ public void onEnable() { ? new VanishManagerImpl(matchManager, executorService) : new NoopVanishManager(); + inventoryManager = new InventoryManager(this); + inventoryManager.init(); + if (config.showTabList()) { matchTabManager = new MatchTabManager(this); } @@ -322,6 +327,11 @@ public VanishManager getVanishManager() { return vanishManager; } + @Override + public InventoryManager getInventoryManager() { + return inventoryManager; + } + private void registerCommands() { final CommandGraph graph = config.isCommunityMode() ? new CommunityCommandGraph() : new CommandGraph(); diff --git a/core/src/main/java/tc/oc/pgm/api/Modules.java b/core/src/main/java/tc/oc/pgm/api/Modules.java index 54525c743c..39606acb60 100644 --- a/core/src/main/java/tc/oc/pgm/api/Modules.java +++ b/core/src/main/java/tc/oc/pgm/api/Modules.java @@ -76,6 +76,7 @@ import tc.oc.pgm.modules.ModifyBowProjectileMatchModule; import tc.oc.pgm.modules.ModifyBowProjectileModule; import tc.oc.pgm.modules.MultiTradeMatchModule; +import tc.oc.pgm.modules.PlayerTimeMatchModule; import tc.oc.pgm.modules.SoundsMatchModule; import tc.oc.pgm.modules.ToolRepairMatchModule; import tc.oc.pgm.modules.ToolRepairModule; @@ -159,6 +160,7 @@ static void registerAll() { register(StatsMatchModule.class, StatsMatchModule::new); register(MapmakerMatchModule.class, MapmakerMatchModule::new); register(TNTRenderMatchModule.class, TNTRenderMatchModule::new); + register(PlayerTimeMatchModule.class, PlayerTimeMatchModule::new); // FIXME: Disabled due to lag - look into future optimization // register(ProjectileTrailMatchModule.class, ProjectileTrailMatchModule::new); diff --git a/core/src/main/java/tc/oc/pgm/api/PGM.java b/core/src/main/java/tc/oc/pgm/api/PGM.java index 947ff302e0..92c24b9d03 100644 --- a/core/src/main/java/tc/oc/pgm/api/PGM.java +++ b/core/src/main/java/tc/oc/pgm/api/PGM.java @@ -2,6 +2,7 @@ import static com.google.common.base.Preconditions.checkNotNull; +import fr.minuskube.inv.InventoryManager; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.atomic.AtomicReference; import java.util.logging.Logger; @@ -40,6 +41,8 @@ public interface PGM extends Plugin { VanishManager getVanishManager(); + InventoryManager getInventoryManager(); + AtomicReference GLOBAL = new AtomicReference<>(null); static PGM set(PGM pgm) { diff --git a/core/src/main/java/tc/oc/pgm/api/setting/SettingKey.java b/core/src/main/java/tc/oc/pgm/api/setting/SettingKey.java index 4525d63bf2..4d68b4a31c 100644 --- a/core/src/main/java/tc/oc/pgm/api/setting/SettingKey.java +++ b/core/src/main/java/tc/oc/pgm/api/setting/SettingKey.java @@ -7,7 +7,9 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +import org.bukkit.Material; import tc.oc.pgm.api.player.MatchPlayer; +import tc.oc.pgm.modules.PlayerTimeMatchModule; /** * A toggleable setting with various possible {@link SettingValue}s. @@ -15,37 +17,76 @@ * @see SettingValue */ public enum SettingKey { - CHAT("chat", CHAT_TEAM, CHAT_GLOBAL, CHAT_ADMIN), // Changes the default chat channel + CHAT( + "chat", + Material.SIGN, + CHAT_TEAM, + CHAT_GLOBAL, + CHAT_ADMIN), // Changes the default chat channel DEATH( - Arrays.asList("death", "dms"), DEATH_ALL, DEATH_OWN), // Changes which death messages are seen - PICKER("picker", PICKER_AUTO, PICKER_ON, PICKER_OFF), // Changes when the picker is displayed - JOIN(Arrays.asList("join", "jms"), JOIN_ON, JOIN_OFF), // Changes if join messages are seen + Arrays.asList("death", "dms"), + Material.SKULL_ITEM, + DEATH_ALL, + DEATH_OWN), // Changes which death messages are seen + PICKER( + "picker", + Material.LEATHER_HELMET, + PICKER_AUTO, + PICKER_ON, + PICKER_OFF), // Changes when the picker is displayed + JOIN( + Arrays.asList("join", "jms"), + Material.WOOD_DOOR, + JOIN_ON, + JOIN_OFF), // Changes if join messages are seen MESSAGE( Arrays.asList("message", "dm"), + Material.BOOK_AND_QUILL, MESSAGE_ON, MESSAGE_OFF), // Changes if direct messages are accepted - OBSERVERS(Arrays.asList("observers", "obs"), OBSERVERS_ON, OBSERVERS_OFF) { + OBSERVERS(Arrays.asList("observers", "obs"), Material.EYE_OF_ENDER, OBSERVERS_ON, OBSERVERS_OFF) { @Override public void update(MatchPlayer player) { player.resetVisibility(); } }, // Changes if observers are visible - SOUNDS("sounds", SOUNDS_ALL, SOUNDS_DM, SOUNDS_NONE), // Changes when sounds are played - VOTE("vote", VOTE_ON, VOTE_OFF), // Changes if the vote book is shown on cycle - STATS("stats", STATS_ON, STATS_OFF), // Changes if stats are tracked - EFFECTS("effects", EFFECTS_ON, EFFECTS_OFF) // Changes if special particle effects are shown -; + SOUNDS( + "sounds", + Material.NOTE_BLOCK, + SOUNDS_ALL, + SOUNDS_DM, + SOUNDS_NONE), // Changes when sounds are played + VOTE( + "vote", + Material.ENCHANTED_BOOK, + VOTE_ON, + VOTE_OFF), // Changes if the vote book is shown on cycle + STATS("stats", Material.PAPER, STATS_ON, STATS_OFF), // Changes if stats are tracked + EFFECTS( + "effects", + Material.FIREWORK, + EFFECTS_ON, + EFFECTS_OFF), // Changes if special particle effects are shown + TIME(Arrays.asList("time", "theme"), Material.WATCH, TIME_AUTO, TIME_DARK, TIME_LIGHT) { + @Override + public void update(MatchPlayer player) { + PlayerTimeMatchModule.updatePlayerTime(player); + } + }; // Changes player preference for time of day + ; private final List aliases; private final SettingValue[] values; + private final Material icon; - SettingKey(String name, SettingValue... values) { - this(Collections.singletonList(name), values); + SettingKey(String name, Material icon, SettingValue... values) { + this(Collections.singletonList(name), icon, values); } - SettingKey(List aliases, SettingValue... values) { + SettingKey(List aliases, Material icon, SettingValue... values) { checkArgument(!aliases.isEmpty(), "aliases is empty"); this.aliases = ImmutableList.copyOf(aliases); + this.icon = icon; this.values = values; } @@ -85,6 +126,15 @@ public SettingValue getDefaultValue() { return getPossibleValues()[0]; } + /** + * Get the {@link Material} used to visually represent this setting in GUI menus. + * + * @return {@link Material} to visually represent setting. + */ + public Material getIconMaterial() { + return icon; + } + @Override public String toString() { return getName(); diff --git a/core/src/main/java/tc/oc/pgm/api/setting/SettingValue.java b/core/src/main/java/tc/oc/pgm/api/setting/SettingValue.java index c36d518e40..54cee49279 100644 --- a/core/src/main/java/tc/oc/pgm/api/setting/SettingValue.java +++ b/core/src/main/java/tc/oc/pgm/api/setting/SettingValue.java @@ -6,6 +6,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import javax.annotation.Nullable; +import org.bukkit.DyeColor; import tc.oc.pgm.util.StringUtils; /** @@ -14,46 +15,51 @@ * @see SettingKey */ public enum SettingValue { - CHAT_TEAM("chat", "team"), // Only send to members on the player's team - CHAT_GLOBAL("chat", "global"), // Send to all players in the same match - CHAT_ADMIN("chat", "admin"), // Send to all server operators + CHAT_TEAM("chat", "team", DyeColor.GREEN), // Only send to members on the player's team + CHAT_GLOBAL("chat", "global", DyeColor.ORANGE), // Send to all players in the same match + CHAT_ADMIN("chat", "admin", DyeColor.RED), // Send to all server operators - DEATH_OWN("death", "own"), // Only send death messages involving self - DEATH_ALL("death", "all"), // Send all death messages, highlight your own + DEATH_OWN("death", "own", DyeColor.RED), // Only send death messages involving self + DEATH_ALL("death", "all", DyeColor.GREEN), // Send all death messages, highlight your own - PICKER_AUTO("picker", "auto"), // Display after cycle, or with permissions. - PICKER_ON("picker", "on"), // Display the picker GUI always - PICKER_OFF("picker", "off"), // Never display the picker GUI + PICKER_AUTO("picker", "auto", DyeColor.ORANGE), // Display after cycle, or with permissions. + PICKER_ON("picker", "on", DyeColor.GREEN), // Display the picker GUI always + PICKER_OFF("picker", "off", DyeColor.RED), // Never display the picker GUI - JOIN_ON("join", "all"), // Send all join messages - JOIN_OFF("join", "none"), // Never send join messages + JOIN_ON("join", "all", DyeColor.ORANGE), // Send all join messages + JOIN_OFF("join", "none", DyeColor.RED), // Never send join messages - MESSAGE_ON("message", "on"), // Always accept direct messages - MESSAGE_OFF("message", "off"), // Never accept direct messages + MESSAGE_ON("message", "on", DyeColor.GREEN), // Always accept direct messages + MESSAGE_OFF("message", "off", DyeColor.RED), // Never accept direct messages - OBSERVERS_ON("observers", "on"), // Show observers - OBSERVERS_OFF("observers", "off"), // Hide observers + OBSERVERS_ON("observers", "on", DyeColor.GREEN), // Show observers + OBSERVERS_OFF("observers", "off", DyeColor.RED), // Hide observers - SOUNDS_ALL("sounds", "all"), // Play all sounds - SOUNDS_DM("sounds", "messages"), // Only play DM sounds - SOUNDS_NONE("sounds", "none"), // Never play sounds + SOUNDS_ALL("sounds", "all", DyeColor.GREEN), // Play all sounds + SOUNDS_DM("sounds", "messages", DyeColor.ORANGE), // Only play DM sounds + SOUNDS_NONE("sounds", "none", DyeColor.RED), // Never play sounds - VOTE_ON("vote", "on"), // Show the vote book on cycle - VOTE_OFF("vote", "off"), // Don't show the vote book on cycle + VOTE_ON("vote", "on", DyeColor.GREEN), // Show the vote book on cycle + VOTE_OFF("vote", "off", DyeColor.RED), // Don't show the vote book on cycle - STATS_ON("stats", "on"), // Track stats - STATS_OFF("stats", "off"), // Don't track stats + STATS_ON("stats", "on", DyeColor.GREEN), // Track stats + STATS_OFF("stats", "off", DyeColor.RED), // Don't track stats - EFFECTS_ON("effects", "on"), // Display special particle effects - EFFECTS_OFF("effects", "off"); // Don't display special particle effects - ; + EFFECTS_ON("effects", "on", DyeColor.GREEN), // Display special particle effects + EFFECTS_OFF("effects", "off", DyeColor.RED), // Don't display special particle effects + + TIME_AUTO("time", "auto", DyeColor.ORANGE), // Player time is in sync + TIME_DARK("time", "dark", DyeColor.GRAY), // Player time is always set to midday + TIME_LIGHT("time", "light", DyeColor.WHITE); // Player time is always set to midnight private final String key; private final String name; + private final DyeColor color; - SettingValue(String group, String name) { + SettingValue(String group, String name, DyeColor color) { this.key = checkNotNull(group); this.name = checkNotNull(name); + this.color = checkNotNull(color); } /** @@ -74,6 +80,16 @@ public String getName() { return name; } + /** + * Get {@link DyeColor} related to this setting value . + * + * @see {@link SettingMenu} for usage. + * @return {@link DyeColor} for this setting value. + */ + public DyeColor getColor() { + return color; + } + @Override public String toString() { return getName(); diff --git a/core/src/main/java/tc/oc/pgm/command/SettingCommand.java b/core/src/main/java/tc/oc/pgm/command/SettingCommand.java index 9f965d8cd6..aa2889f303 100644 --- a/core/src/main/java/tc/oc/pgm/command/SettingCommand.java +++ b/core/src/main/java/tc/oc/pgm/command/SettingCommand.java @@ -15,20 +15,28 @@ import tc.oc.pgm.api.setting.SettingValue; import tc.oc.pgm.api.setting.Settings; import tc.oc.pgm.observers.ObserverToolsMatchModule; +import tc.oc.pgm.settings.SettingsMenu; import tc.oc.pgm.util.text.TextFormatter; // TODO: remove some of these when settings UI is released public final class SettingCommand { @Command( - aliases = {"settings", "tools", "observertools", "ot"}, + aliases = {"settings"}, desc = "Open the settings menu") public void settings(MatchPlayer player) { + new SettingsMenu(player); + } + + @Command( + aliases = {"tools", "observertools", "ot"}, + desc = "Open the observer tools menu") + public void observerTools(MatchPlayer player) { if (player.isObserving()) { final ObserverToolsMatchModule tools = player.getMatch().getModule(ObserverToolsMatchModule.class); if (tools != null) { - tools.openMenuManual(player); + tools.openMenu(player); } } else { // TODO: reconsider when observer tools become settings diff --git a/core/src/main/java/tc/oc/pgm/menu/InventoryMenu.java b/core/src/main/java/tc/oc/pgm/menu/InventoryMenu.java new file mode 100644 index 0000000000..cb93367ddb --- /dev/null +++ b/core/src/main/java/tc/oc/pgm/menu/InventoryMenu.java @@ -0,0 +1,97 @@ +package tc.oc.pgm.menu; + +import static net.kyori.adventure.text.Component.translatable; +import static tc.oc.pgm.util.text.TextTranslations.translateLegacy; + +import fr.minuskube.inv.ClickableItem; +import fr.minuskube.inv.SmartInventory; +import fr.minuskube.inv.content.InventoryContents; +import fr.minuskube.inv.content.InventoryProvider; +import javax.annotation.Nullable; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextDecoration; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import tc.oc.pgm.api.PGM; +import tc.oc.pgm.api.player.MatchPlayer; +import tc.oc.pgm.util.inventory.ItemBuilder; + +/** A generic inventory menu * */ +public abstract class InventoryMenu implements InventoryProvider { + + private final SmartInventory inventory; + private final MatchPlayer viewer; + + public InventoryMenu(String titleKey, NamedTextColor titleColor, int rows, MatchPlayer viewer) { + this(titleKey, titleColor, rows, viewer, null); + } + + public InventoryMenu( + String titleKey, + NamedTextColor titleColor, + int rows, + MatchPlayer viewer, + @Nullable SmartInventory parent) { + this(translatable(titleKey, titleColor, TextDecoration.BOLD), rows, viewer, parent); + } + + public InventoryMenu( + Component title, int rows, MatchPlayer viewer, @Nullable SmartInventory parent) { + this.viewer = viewer; + + SmartInventory.Builder builder = + SmartInventory.builder() + .manager(PGM.get().getInventoryManager()) + .title(translateLegacy(title, getBukkit())) + .size(rows, 9) + .provider(this); + + if (parent != null) { + builder.parent(parent); + } + + this.inventory = builder.build(); + } + + public void open() { + inventory.open(getBukkit()); + } + + public Player getBukkit() { + return viewer.getBukkit(); + } + + public MatchPlayer getViewer() { + return viewer; + } + + public SmartInventory getInventory() { + return inventory; + } + + public void addBackButton( + InventoryContents contents, Component parentMenuTitle, int row, int col) { + Component back = translatable("menu.page.return", NamedTextColor.GRAY, parentMenuTitle); + + contents.set( + row, + col, + ClickableItem.of( + new ItemBuilder() + .material(Material.BARRIER) + .name(translateLegacy(back, getBukkit())) + .build(), + c -> { + getInventory() + .getParent() + .ifPresent( + parent -> { + parent.open(getBukkit()); + }); + })); + } + + @Override + public void update(Player player, InventoryContents contents) {} +} diff --git a/util/src/main/java/tc/oc/pgm/util/menu/InventoryMenuItem.java b/core/src/main/java/tc/oc/pgm/menu/MenuItem.java similarity index 53% rename from util/src/main/java/tc/oc/pgm/util/menu/InventoryMenuItem.java rename to core/src/main/java/tc/oc/pgm/menu/MenuItem.java index 0f77be3833..38e0d7e4b0 100644 --- a/util/src/main/java/tc/oc/pgm/util/menu/InventoryMenuItem.java +++ b/core/src/main/java/tc/oc/pgm/menu/MenuItem.java @@ -1,18 +1,21 @@ -package tc.oc.pgm.util.menu; +package tc.oc.pgm.menu; +import fr.minuskube.inv.ClickableItem; import java.util.List; +import java.util.function.Consumer; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.TextDecoration; import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.event.inventory.ClickType; +import org.bukkit.event.inventory.InventoryClickEvent; import org.bukkit.inventory.ItemFlag; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; import tc.oc.pgm.util.text.TextTranslations; -/** @see InventoryMenu */ -public interface InventoryMenuItem { +/** @see InventoryMenu * */ +public interface MenuItem { /** * Gets the display name of this item, can be localized, colored, and decorated (will @@ -27,10 +30,28 @@ public interface InventoryMenuItem { Material getMaterial(Player player); /** - * If this method is in a {@link InventoryMenu} this method will be called automatically by the - * menu + * Action that should be performed once item is clicked + * + * @param player The player who clicked + * @param type The type of click */ - void onInventoryClick(InventoryMenu menu, Player player, ClickType clickType); + void onClick(Player player, ClickType type); + + /** + * Gets the action to perform when this item is clicked in an inventory menu. + * + *

Note: only override if you need access to the {@link InventoryClickEvent} otherwise use + * {@link MenuItem#onClick(Player, ClickType)} + * + * @return a consumer for the inventory click event + */ + default Consumer getAction() { + return context -> { + Player player = (Player) context.getWhoClicked(); + onClick(player, context.getClick()); + context.setCurrentItem(createItem(player)); + }; + } /** * Called by {@link #createItem(Player)} after standard changes has been done. When possible this @@ -40,6 +61,16 @@ default ItemMeta modifyMeta(ItemMeta meta) { return meta; } + /** + * Creates a {@link ClickableItem} for the given player Item & action are linked together here + * + * @param player The player to display this item to + * @return a clickable item used in inventory menus + */ + default ClickableItem getClickableItem(Player player) { + return ClickableItem.of(createItem(player), getAction()); + } + default ItemStack createItem(Player player) { ItemStack stack = new ItemStack(getMaterial(player)); ItemMeta meta = stack.getItemMeta(); diff --git a/core/src/main/java/tc/oc/pgm/menu/PagedInventoryMenu.java b/core/src/main/java/tc/oc/pgm/menu/PagedInventoryMenu.java new file mode 100644 index 0000000000..f9d2abbf9c --- /dev/null +++ b/core/src/main/java/tc/oc/pgm/menu/PagedInventoryMenu.java @@ -0,0 +1,138 @@ +package tc.oc.pgm.menu; + +import static net.kyori.adventure.text.Component.translatable; +import static tc.oc.pgm.util.text.TextTranslations.translateLegacy; + +import fr.minuskube.inv.ClickableItem; +import fr.minuskube.inv.SmartInventory; +import fr.minuskube.inv.content.InventoryContents; +import fr.minuskube.inv.content.Pagination; +import fr.minuskube.inv.content.SlotIterator; +import fr.minuskube.inv.content.SlotPos; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextDecoration; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; +import tc.oc.pgm.api.player.MatchPlayer; +import tc.oc.pgm.util.bukkit.BukkitUtils; +import tc.oc.pgm.util.inventory.ItemBuilder; + +/** A specialized inventory menu for easy pagination * */ +public abstract class PagedInventoryMenu extends InventoryMenu { + + private static final Material PAGE_MATERIAL = Material.ARROW; + + private final int itemsPerPage; + private final int startingRow; + private final int startingCol; + + public PagedInventoryMenu( + Component title, + int rows, + MatchPlayer viewer, + SmartInventory parent, + int itemsPerPage, + int startingRow, + int startingCol) { + super(title, rows, viewer, parent); + this.itemsPerPage = itemsPerPage; + this.startingRow = startingRow; + this.startingCol = startingCol; + } + + /** + * Automatic setup of page contents + * + *

To enable paged setup call in {@link #init(Player, InventoryContents)} + * + * @param player {@link Player} viewing the inventory + * @param contents {@link InventoryContents} of menu + */ + public void setupPageContents(Player player, InventoryContents contents) { + ClickableItem[] items = getPageContents(player); + + // If no items are found, display empty contents button + if (items == null || items.length == 0) { + contents.set(getEmptyPageSlot(), getEmptyContentsButton(player)); + return; + } + + // Setup pagination + Pagination page = contents.pagination(); + page.setItems(items); + page.setItemsPerPage(itemsPerPage); + page.addToIterator( + contents.newIterator(SlotIterator.Type.HORIZONTAL, startingRow, startingCol)); + + // Previous button + if (!page.isFirst()) { + contents.set( + getPreviousPageSlot(), getPageItem(player, page.getPage() - 1, "menu.page.previous")); + } + + // Next button + if (!page.isLast()) { + contents.set(getNextPageSlot(), getPageItem(player, page.getPage() + 1, "menu.page.next")); + } + } + + /** + * The position of the previous page button. + * + * @return a {@link SlotPos} where previous page button will be located. + */ + public abstract SlotPos getPreviousPageSlot(); + + /** + * The position of the next page button. + * + * @return a {@link SlotPos} where next page button will be located. + */ + public abstract SlotPos getNextPageSlot(); + + /** + * The position of the no page contents button, only displayed when page contents is empty or + * null. + * + * @see #getPageContents(Player) + * @return a {@link SlotPos} where no page button will be located. + */ + public abstract SlotPos getEmptyPageSlot(); + + /** + * An array of {@link ClickableItem}s that will populate the paged inventory + * + * @param viewer The {@link Player} viewer who each item will be rendered for + * @return an array of clickable items + */ + public abstract ClickableItem[] getPageContents(Player viewer); + + protected ClickableItem getEmptyContentsButton(Player viewer) { + Component name = translatable("menu.page.empty", NamedTextColor.DARK_RED, TextDecoration.BOLD); + return ClickableItem.empty( + new ItemBuilder().material(Material.BARRIER).name(translateLegacy(name, viewer)).build()); + } + + protected ClickableItem getPageItem(Player player, int page, String key) { + Component text = translatable(key, NamedTextColor.YELLOW, TextDecoration.BOLD); + return ClickableItem.of( + getPageIcon(translateLegacy(text, player), page + 1), + c -> getInventory().open(player, page)); + } + + private final ItemStack getPageIcon(String text, int page) { + return getNamedItem(text, PAGE_MATERIAL, page); + } + + private ItemStack getNamedItem(String text, Material material, int amount) { + return new ItemBuilder() + .material(material) + .amount(amount) + .name(BukkitUtils.colorize(text)) + .flags(ItemFlag.values()) + .build(); + } +} diff --git a/core/src/main/java/tc/oc/pgm/modules/PlayerTimeMatchModule.java b/core/src/main/java/tc/oc/pgm/modules/PlayerTimeMatchModule.java new file mode 100644 index 0000000000..8311f403d8 --- /dev/null +++ b/core/src/main/java/tc/oc/pgm/modules/PlayerTimeMatchModule.java @@ -0,0 +1,47 @@ +package tc.oc.pgm.modules; + +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import tc.oc.pgm.api.match.Match; +import tc.oc.pgm.api.match.MatchModule; +import tc.oc.pgm.api.match.MatchScope; +import tc.oc.pgm.api.player.MatchPlayer; +import tc.oc.pgm.api.player.event.MatchPlayerAddEvent; +import tc.oc.pgm.api.setting.SettingKey; +import tc.oc.pgm.api.setting.SettingValue; +import tc.oc.pgm.events.ListenerScope; + +@ListenerScope(MatchScope.LOADED) +public class PlayerTimeMatchModule implements MatchModule, Listener { + + private final Match match; + + public PlayerTimeMatchModule(Match match) { + this.match = match; + } + + @EventHandler + public void onPlayerAdded(MatchPlayerAddEvent event) { + updatePlayerTime(event.getPlayer()); + // Upon being added to a match, set the player's preferred time + } + + public static void updatePlayerTime(MatchPlayer player) { + if (player.getSettings().getValue(SettingKey.TIME).equals(SettingValue.TIME_AUTO)) { + player.getBukkit().resetPlayerTime(); + return; + } + player.getBukkit().setPlayerTime(getPreferencedTime(player), false); + } + + private static long getPreferencedTime(MatchPlayer player) { + switch (player.getSettings().getValue(SettingKey.TIME)) { + case TIME_DARK: + return 18000; // Midnight + case TIME_LIGHT: + return 6000; // Midday + default: + return player.getWorld().getFullTime(); + } + } +} diff --git a/core/src/main/java/tc/oc/pgm/observers/ObserverToolsInventoryMenuItem.java b/core/src/main/java/tc/oc/pgm/observers/ObserverToolsInventoryMenuItem.java deleted file mode 100644 index 34e29bf119..0000000000 --- a/core/src/main/java/tc/oc/pgm/observers/ObserverToolsInventoryMenuItem.java +++ /dev/null @@ -1,45 +0,0 @@ -package tc.oc.pgm.observers; - -import static net.kyori.adventure.text.Component.translatable; - -import com.google.common.collect.Lists; -import java.util.List; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.format.NamedTextColor; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import tc.oc.pgm.util.menu.InventoryMenu; -import tc.oc.pgm.util.menu.InventoryMenuItem; -import tc.oc.pgm.util.text.TextTranslations; - -public class ObserverToolsInventoryMenuItem implements InventoryMenuItem { - - private final InventoryMenu observerToolsMenu; - - public ObserverToolsInventoryMenuItem(InventoryMenu observerToolsMenu) { - this.observerToolsMenu = observerToolsMenu; - } - - @Override - public Component getDisplayName() { - return translatable("setting.displayName", NamedTextColor.AQUA); - } - - @Override - public List getLore(Player player) { - return Lists.newArrayList( - TextTranslations.translateLegacy( - translatable("setting.lore", NamedTextColor.GRAY), player)); - } - - @Override - public Material getMaterial(Player player) { - return ObserverToolsMatchModule.TOOL_MATERIAL; - } - - @Override - public void onInventoryClick(InventoryMenu menu, Player player, ClickType clickType) { - observerToolsMenu.display(player); - } -} diff --git a/core/src/main/java/tc/oc/pgm/observers/ObserverToolsMatchModule.java b/core/src/main/java/tc/oc/pgm/observers/ObserverToolsMatchModule.java index b2ec3e2342..492d0307d3 100644 --- a/core/src/main/java/tc/oc/pgm/observers/ObserverToolsMatchModule.java +++ b/core/src/main/java/tc/oc/pgm/observers/ObserverToolsMatchModule.java @@ -2,15 +2,16 @@ import static net.kyori.adventure.text.Component.translatable; -import com.google.common.collect.ImmutableList; -import java.util.List; +import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextDecoration; import org.bukkit.Material; +import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.block.Action; -import org.bukkit.event.inventory.ClickType; import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.ItemFlag; import org.bukkit.inventory.ItemStack; import tc.oc.pgm.api.match.Match; import tc.oc.pgm.api.match.MatchModule; @@ -19,14 +20,9 @@ import tc.oc.pgm.api.module.exception.ModuleLoadException; import tc.oc.pgm.api.player.MatchPlayer; import tc.oc.pgm.events.ListenerScope; -import tc.oc.pgm.observers.tools.FlySpeedTool; -import tc.oc.pgm.observers.tools.GamemodeTool; -import tc.oc.pgm.observers.tools.NightVisionTool; -import tc.oc.pgm.observers.tools.VisibilityTool; import tc.oc.pgm.spawns.events.ObserverKitApplyEvent; -import tc.oc.pgm.util.menu.InventoryMenu; -import tc.oc.pgm.util.menu.InventoryMenuItem; -import tc.oc.pgm.util.menu.pattern.SingleRowMenuArranger; +import tc.oc.pgm.util.inventory.ItemBuilder; +import tc.oc.pgm.util.text.TextTranslations; @ListenerScope(MatchScope.LOADED) public class ObserverToolsMatchModule implements MatchModule, Listener { @@ -38,31 +34,16 @@ public ObserverToolsMatchModule createMatchModule(Match match) throws ModuleLoad } } - // Slot where tool item is placed - public static final int TOOL_BUTTON_SLOT = 8; - - // Material of tool item item + public static final int TOOL_SLOT = 8; public static final Material TOOL_MATERIAL = Material.DIAMOND; + public static final Component TOOL_NAME = + translatable("setting.displayName", NamedTextColor.AQUA, TextDecoration.BOLD); + public static final Component TOOL_LORE = translatable("setting.lore", NamedTextColor.GRAY); private final Match match; - private final InventoryMenu menu; - private final InventoryMenuItem toolItem; public ObserverToolsMatchModule(Match match) { this.match = match; - - final List tools = - ImmutableList.of( - new FlySpeedTool(), new NightVisionTool(), new VisibilityTool(), new GamemodeTool()); - - this.menu = - new InventoryMenu( - match.getWorld(), - translatable("setting.title", NamedTextColor.AQUA), - tools, - new SingleRowMenuArranger()); - - this.toolItem = new ObserverToolsInventoryMenuItem(this.menu); } @EventHandler @@ -75,30 +56,39 @@ public void onToolClick(PlayerInteractEvent event) { if (isRightClick(event.getAction())) { ItemStack item = event.getPlayer().getItemInHand(); MatchPlayer player = match.getPlayer(event.getPlayer()); - - if (item.getType().equals(TOOL_MATERIAL) && player != null && canUse(player)) { - this.toolItem.onInventoryClick(null, event.getPlayer(), ClickType.RIGHT); + if (player != null && item != null && item.isSimilar(createItem(player.getBukkit()))) { + openMenu(player); } } } - public void openMenuManual(MatchPlayer player) { + public void openMenu(MatchPlayer player) { if (canUse(player)) { - menu.display(player.getBukkit()); + new ObserverToolsMenu(player); } } - private boolean canUse(MatchPlayer player) { - return player.isObserving(); - } - private void refreshKit(MatchPlayer player) { if (canUse(player)) { - player.getInventory().setItem(TOOL_BUTTON_SLOT, toolItem.createItem(player.getBukkit())); + player.getInventory().setItem(TOOL_SLOT, createItem(player.getBukkit())); } } + private boolean canUse(MatchPlayer player) { + return player.isObserving(); + } + private boolean isRightClick(Action action) { return action == Action.RIGHT_CLICK_AIR || action == Action.RIGHT_CLICK_BLOCK; } + + private ItemStack createItem(Player player) { + return new ItemBuilder() + .material(TOOL_MATERIAL) + .amount(1) + .name(TextTranslations.translateLegacy(TOOL_NAME, player)) + .lore(TextTranslations.translateLegacy(TOOL_LORE, player)) + .flags(ItemFlag.values()) + .build(); + } } diff --git a/core/src/main/java/tc/oc/pgm/observers/ObserverToolsMenu.java b/core/src/main/java/tc/oc/pgm/observers/ObserverToolsMenu.java new file mode 100644 index 0000000000..38bffebf62 --- /dev/null +++ b/core/src/main/java/tc/oc/pgm/observers/ObserverToolsMenu.java @@ -0,0 +1,36 @@ +package tc.oc.pgm.observers; + +import com.google.common.collect.Lists; +import fr.minuskube.inv.content.InventoryContents; +import java.util.List; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.entity.Player; +import tc.oc.pgm.api.player.MatchPlayer; +import tc.oc.pgm.menu.InventoryMenu; +import tc.oc.pgm.menu.MenuItem; +import tc.oc.pgm.observers.tools.FlySpeedTool; +import tc.oc.pgm.observers.tools.GamemodeTool; +import tc.oc.pgm.observers.tools.NightVisionTool; +import tc.oc.pgm.observers.tools.SettingsTool; + +public class ObserverToolsMenu extends InventoryMenu { + + private List items; + + public ObserverToolsMenu(MatchPlayer viewer) { + super("setting.title", NamedTextColor.AQUA, 1, viewer); + this.items = + Lists.newArrayList( + new FlySpeedTool(), new NightVisionTool(), new SettingsTool(), new GamemodeTool()); + open(); + } + + @Override + public void init(Player player, InventoryContents contents) { + int col = 1; + for (MenuItem item : items) { + contents.set(0, col, item.getClickableItem(player)); + col += 2; + } + } +} diff --git a/core/src/main/java/tc/oc/pgm/observers/tools/FlySpeedTool.java b/core/src/main/java/tc/oc/pgm/observers/tools/FlySpeedTool.java index 286bd8c1f9..3dcaa74869 100644 --- a/core/src/main/java/tc/oc/pgm/observers/tools/FlySpeedTool.java +++ b/core/src/main/java/tc/oc/pgm/observers/tools/FlySpeedTool.java @@ -10,11 +10,10 @@ import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.event.inventory.ClickType; -import tc.oc.pgm.util.menu.InventoryMenu; -import tc.oc.pgm.util.menu.InventoryMenuItem; +import tc.oc.pgm.menu.MenuItem; import tc.oc.pgm.util.text.TextTranslations; -public class FlySpeedTool implements InventoryMenuItem { +public class FlySpeedTool implements MenuItem { private static final String TRANSLATION_KEY = "setting.flyspeed."; @@ -36,14 +35,13 @@ public Material getMaterial(Player player) { } @Override - public void onInventoryClick(InventoryMenu menu, Player player, ClickType clickType) { + public void onClick(Player player, ClickType click) { FlySpeed speed = FlySpeed.of(player.getFlySpeed()); - if (clickType.isRightClick()) { + if (click.isRightClick()) { player.setFlySpeed(speed.getPrev().getValue()); } else { player.setFlySpeed(speed.getNext().getValue()); } - menu.refreshWindow(player); } public enum FlySpeed { diff --git a/core/src/main/java/tc/oc/pgm/observers/tools/GamemodeTool.java b/core/src/main/java/tc/oc/pgm/observers/tools/GamemodeTool.java index cffcb07e80..27f796ddb3 100644 --- a/core/src/main/java/tc/oc/pgm/observers/tools/GamemodeTool.java +++ b/core/src/main/java/tc/oc/pgm/observers/tools/GamemodeTool.java @@ -14,12 +14,11 @@ import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.event.inventory.ClickType; +import tc.oc.pgm.menu.MenuItem; import tc.oc.pgm.util.Audience; -import tc.oc.pgm.util.menu.InventoryMenu; -import tc.oc.pgm.util.menu.InventoryMenuItem; import tc.oc.pgm.util.text.TextTranslations; -public class GamemodeTool implements InventoryMenuItem { +public class GamemodeTool implements MenuItem { @Override public Component getDisplayName() { @@ -29,7 +28,7 @@ public Component getDisplayName() { @Override public List getLore(Player player) { Component gamemode = - translatable("gameMode." + player.getGameMode().name().toLowerCase(), NamedTextColor.AQUA); + translatable("misc." + player.getGameMode().name().toLowerCase(), NamedTextColor.AQUA); Component lore = translatable("setting.gamemode.lore", NamedTextColor.GRAY, gamemode); return Lists.newArrayList(TextTranslations.translateLegacy(lore, player)); } @@ -40,12 +39,7 @@ public Material getMaterial(Player player) { } @Override - public void onInventoryClick(InventoryMenu menu, Player player, ClickType clickType) { - toggleObserverGameMode(player); - menu.refreshWindow(player); - } - - public void toggleObserverGameMode(Player player) { + public void onClick(Player player, ClickType click) { player.setGameMode(getOppositeMode(player.getGameMode())); if (player.getGameMode() == GameMode.SPECTATOR) { Audience.get(player).sendWarning(getToggleMessage()); diff --git a/core/src/main/java/tc/oc/pgm/observers/tools/NightVisionTool.java b/core/src/main/java/tc/oc/pgm/observers/tools/NightVisionTool.java index f5e22d45d1..cf6aa756e6 100644 --- a/core/src/main/java/tc/oc/pgm/observers/tools/NightVisionTool.java +++ b/core/src/main/java/tc/oc/pgm/observers/tools/NightVisionTool.java @@ -11,11 +11,10 @@ import org.bukkit.event.inventory.ClickType; import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; -import tc.oc.pgm.util.menu.InventoryMenu; -import tc.oc.pgm.util.menu.InventoryMenuItem; +import tc.oc.pgm.menu.MenuItem; import tc.oc.pgm.util.text.TextTranslations; -public class NightVisionTool implements InventoryMenuItem { +public class NightVisionTool implements MenuItem { @Override public Component getDisplayName() { @@ -38,16 +37,7 @@ public Material getMaterial(Player player) { } @Override - public void onInventoryClick(InventoryMenu menu, Player player, ClickType clickType) { - toggleNightVision(player); - menu.refreshWindow(player); - } - - private boolean hasNightVision(Player player) { - return player.hasPotionEffect(PotionEffectType.NIGHT_VISION); - } - - public void toggleNightVision(Player player) { + public void onClick(Player player, ClickType click) { if (hasNightVision(player)) { player.removePotionEffect(PotionEffectType.NIGHT_VISION); } else { @@ -55,4 +45,8 @@ public void toggleNightVision(Player player) { new PotionEffect(PotionEffectType.NIGHT_VISION, Integer.MAX_VALUE, 0, true, false)); } } + + private boolean hasNightVision(Player player) { + return player.hasPotionEffect(PotionEffectType.NIGHT_VISION); + } } diff --git a/core/src/main/java/tc/oc/pgm/observers/tools/SettingsTool.java b/core/src/main/java/tc/oc/pgm/observers/tools/SettingsTool.java new file mode 100644 index 0000000000..ecb55c2eb6 --- /dev/null +++ b/core/src/main/java/tc/oc/pgm/observers/tools/SettingsTool.java @@ -0,0 +1,42 @@ +package tc.oc.pgm.observers.tools; + +import static net.kyori.adventure.text.Component.translatable; + +import com.google.common.collect.Lists; +import java.util.List; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextDecoration; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.ClickType; +import tc.oc.pgm.api.PGM; +import tc.oc.pgm.api.player.MatchPlayer; +import tc.oc.pgm.menu.MenuItem; +import tc.oc.pgm.settings.SettingsMenu; +import tc.oc.pgm.util.text.TextTranslations; + +public class SettingsTool implements MenuItem { + + @Override + public Component getDisplayName() { + return translatable("settings.menu.title", NamedTextColor.AQUA, TextDecoration.BOLD); + } + + @Override + public Material getMaterial(Player player) { + return Material.ANVIL; + } + + @Override + public List getLore(Player player) { + Component lore = translatable("settings.menu.lore", NamedTextColor.GRAY); + return Lists.newArrayList(TextTranslations.translateLegacy(lore, player)); + } + + @Override + public void onClick(Player player, ClickType type) { + MatchPlayer matchPlayer = PGM.get().getMatchManager().getPlayer(player); + new SettingsMenu(matchPlayer); + } +} diff --git a/core/src/main/java/tc/oc/pgm/observers/tools/VisibilityTool.java b/core/src/main/java/tc/oc/pgm/observers/tools/VisibilityTool.java deleted file mode 100644 index fcb5c3487b..0000000000 --- a/core/src/main/java/tc/oc/pgm/observers/tools/VisibilityTool.java +++ /dev/null @@ -1,64 +0,0 @@ -package tc.oc.pgm.observers.tools; - -import static net.kyori.adventure.text.Component.translatable; - -import com.google.common.collect.Lists; -import java.util.List; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.format.NamedTextColor; -import org.bukkit.Material; -import org.bukkit.entity.Player; -import org.bukkit.event.inventory.ClickType; -import tc.oc.pgm.api.PGM; -import tc.oc.pgm.api.player.MatchPlayer; -import tc.oc.pgm.api.setting.SettingKey; -import tc.oc.pgm.api.setting.SettingValue; -import tc.oc.pgm.api.setting.Settings; -import tc.oc.pgm.util.menu.InventoryMenu; -import tc.oc.pgm.util.menu.InventoryMenuItem; -import tc.oc.pgm.util.text.TextTranslations; - -public class VisibilityTool implements InventoryMenuItem { - - @Override - public Component getDisplayName() { - return translatable("setting.visibility", NamedTextColor.YELLOW); - } - - @Override - public List getLore(Player player) { - Component status = - translatable( - isVisible(player) ? "setting.visibility.shown" : "setting.visibility.hidden", - isVisible(player) ? NamedTextColor.GREEN : NamedTextColor.RED); - Component lore = translatable("setting.visibility.lore", NamedTextColor.GRAY, status); - return Lists.newArrayList(TextTranslations.translateLegacy(lore, player)); - } - - @Override - public Material getMaterial(Player player) { - return isVisible(player) ? Material.EYE_OF_ENDER : Material.ENDER_PEARL; - } - - @Override - public void onInventoryClick(InventoryMenu menu, Player player, ClickType clickType) { - toggleObserverVisibility(player); - menu.refreshWindow(player); - } - - public boolean isVisible(Player player) { - return PGM.get() - .getMatchManager() - .getPlayer(player) - .getSettings() - .getValue(SettingKey.OBSERVERS) - == SettingValue.OBSERVERS_ON; - } - - public void toggleObserverVisibility(Player player) { - MatchPlayer matchPlayer = PGM.get().getMatchManager().getPlayer(player); - Settings setting = matchPlayer.getSettings(); - setting.toggleValue(SettingKey.OBSERVERS); - SettingKey.OBSERVERS.update(matchPlayer); - } -} diff --git a/core/src/main/java/tc/oc/pgm/settings/SettingsMenu.java b/core/src/main/java/tc/oc/pgm/settings/SettingsMenu.java new file mode 100644 index 0000000000..daf16c677f --- /dev/null +++ b/core/src/main/java/tc/oc/pgm/settings/SettingsMenu.java @@ -0,0 +1,139 @@ +package tc.oc.pgm.settings; + +import static net.kyori.adventure.text.Component.text; +import static net.kyori.adventure.text.Component.translatable; +import static tc.oc.pgm.util.text.TextTranslations.translateLegacy; + +import fr.minuskube.inv.ClickableItem; +import fr.minuskube.inv.content.InventoryContents; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextDecoration; +import org.apache.commons.lang.WordUtils; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; +import tc.oc.pgm.api.player.MatchPlayer; +import tc.oc.pgm.api.setting.SettingKey; +import tc.oc.pgm.api.setting.SettingValue; +import tc.oc.pgm.menu.InventoryMenu; +import tc.oc.pgm.util.bukkit.BukkitUtils; +import tc.oc.pgm.util.inventory.ItemBuilder; +import tc.oc.pgm.util.text.TextFormatter; + +public class SettingsMenu extends InventoryMenu { + + private static final Component DESC_DIV = text("\u00BB ", NamedTextColor.GOLD); + + public SettingsMenu(MatchPlayer viewer) { + super("settings.menu.title", NamedTextColor.AQUA, 5, viewer); + open(); + } + + @Override + public void init(Player player, InventoryContents contents) { + setup(contents); + } + + @Override + public void update(Player player, InventoryContents contents) { + setup(contents); + } + + // Custom layout: + // Each key shows their related icon w/ description + // followed with by the value represented in a + // colorful way one row under. + // + // Clicking either button will toggle a setting + private void setup(InventoryContents contents) { + int row = 0; + int col = 1; + for (SettingKey setting : SettingKey.values()) { + contents.set(row, col, getSettingKey(setting)); + contents.set(row + 1, col, getSettingValue(setting)); + col++; + if (col > 7) { + row += 3; + col = 1; + } + } + } + + private ClickableItem getSettingKey(SettingKey key) { + return getSettingToggleItem(key, getSettingKeyItem(key)); + } + + private ClickableItem getSettingValue(SettingKey key) { + return getSettingToggleItem(key, getSettingValueItem(key)); + } + + private ClickableItem getSettingToggleItem(SettingKey key, ItemStack item) { + return ClickableItem.of( + item, + c -> { + getViewer().getSettings().toggleValue(key); + key.update(getViewer()); + }); + } + + private ItemStack getSettingKeyItem(SettingKey key) { + Component lore = translatable("settings." + key.name().toLowerCase(), NamedTextColor.GRAY); + return new ItemBuilder() + .material(key.getIconMaterial()) + .name(ChatColor.YELLOW + ChatColor.BOLD.toString() + WordUtils.capitalize(key.getName())) + .lore(translateLegacy(lore, getBukkit())) + .flags(ItemFlag.values()) + .build(); + } + + private ItemStack getSettingValueItem(SettingKey key) { + SettingValue value = getViewer().getSettings().getValue(key); + Component current = + translatable( + "setting.get", NamedTextColor.GRAY, getSettingKeyName(key), getSettingValueName(value)); + Component desc = getSettingDescription(value); + Component toggle = translatable("settings.menu.toggle", NamedTextColor.GRAY); + + return new ItemBuilder() + .material(Material.INK_SACK) + .color(value.getColor()) + .name(ChatColor.YELLOW + ChatColor.BOLD.toString() + WordUtils.capitalize(key.getName())) + .lore( + translateLegacy(current, getBukkit()), + translateLegacy(desc, getBukkit()), + "", + translateLegacy(toggle, getBukkit())) + .flags(ItemFlag.values()) + .build(); + } + + private Component getSettingKeyName(SettingKey key) { + return text(key.getName(), NamedTextColor.YELLOW, TextDecoration.BOLD); + } + + private Component getSettingValueName(SettingValue value) { + return text( + WordUtils.capitalize(value.getName()), + TextFormatter.convert(BukkitUtils.dyeColorToChatColor(value.getColor()))); + } + + private Component getSettingDescription(SettingValue value) { + return text() + .append(DESC_DIV) + .append(translatable(getSettingTranslationKey(value), NamedTextColor.GRAY)) + .build(); + } + + private String getSettingTranslationKey(SettingValue value) { + String valueKey = + value + .name() + .replace(value.getKey().name().toUpperCase(), "") + .replace("_", "") + .toLowerCase(); + return String.format("settings.%s.%s", value.getKey().name().toLowerCase(), valueKey); + } +} diff --git a/core/src/main/java/tc/oc/pgm/stats/StatsMatchModule.java b/core/src/main/java/tc/oc/pgm/stats/StatsMatchModule.java index 15e378644d..bf75fc299a 100644 --- a/core/src/main/java/tc/oc/pgm/stats/StatsMatchModule.java +++ b/core/src/main/java/tc/oc/pgm/stats/StatsMatchModule.java @@ -5,6 +5,7 @@ import static net.kyori.adventure.text.event.HoverEvent.showText; import static tc.oc.pgm.util.text.PlayerComponent.player; +import com.google.common.collect.Lists; import java.text.DecimalFormat; import java.time.Duration; import java.util.ArrayList; @@ -53,15 +54,14 @@ import tc.oc.pgm.flag.event.FlagPickupEvent; import tc.oc.pgm.flag.event.FlagStateChangeEvent; import tc.oc.pgm.flag.state.Carried; +import tc.oc.pgm.stats.menu.StatsMainMenu; +import tc.oc.pgm.stats.menu.items.PlayerStatsMenuItem; +import tc.oc.pgm.stats.menu.items.TeamStatsMenuItem; import tc.oc.pgm.teams.Team; import tc.oc.pgm.teams.TeamMatchModule; import tc.oc.pgm.tracker.TrackerMatchModule; import tc.oc.pgm.tracker.info.ProjectileInfo; import tc.oc.pgm.util.UsernameResolver; -import tc.oc.pgm.util.menu.InventoryMenu; -import tc.oc.pgm.util.menu.InventoryMenuItem; -import tc.oc.pgm.util.menu.pattern.DoubleRowMenuArranger; -import tc.oc.pgm.util.menu.pattern.SingleRowMenuArranger; import tc.oc.pgm.util.named.NameStyle; import tc.oc.pgm.util.nms.NMSHacks; import tc.oc.pgm.util.text.TextFormatter; @@ -76,7 +76,6 @@ public class StatsMatchModule implements MatchModule, Listener { private final Duration showAfter = PGM.get().getConfiguration().showStatsAfter(); private final boolean bestStats = PGM.get().getConfiguration().showBestStats(); private final boolean ownStats = PGM.get().getConfiguration().showOwnStats(); - private final Component verboseStatsTitle = translatable("match.stats.title"); /** Common formats used by stats with decimals */ public static final DecimalFormat TWO_DECIMALS = new DecimalFormat("#.##"); @@ -306,58 +305,49 @@ public void onToolClick(PlayerInteractEvent event) { } } - private List teamItems = null; + public PlayerStatsMenuItem getPlayerStatsItem(MatchPlayer player) { + return new PlayerStatsMenuItem( + player.getId(), + this.getPlayerStat(player), + NMSHacks.getPlayerSkin(player.getBukkit()), + player.getNameLegacy(), + player.getParty().getName().color()); + } + + private List teams; public void giveVerboseStatsItem(MatchPlayer player, boolean forceOpen) { - // Find out if verbose stats is relevant for this match final Collection competitors = match.getSortedCompetitors(); boolean showAllVerboseStats = verboseStats && competitors.stream().allMatch(c -> c instanceof Team); if (!showAllVerboseStats) return; - if (this.teamItems == null) { + if (teams == null) { + teams = Lists.newArrayList(); TeamMatchModule tmm = match.needModule(TeamMatchModule.class); Collection observers = match.getObservers(); - List items = new ArrayList<>(competitors.size()); + for (Competitor competitor : competitors) { Collection relevantObservers = observers.stream() .filter(o -> tmm.getLastTeam(o.getId()) == competitor) .collect(Collectors.toSet()); + Collection relevantOfflinePlayers = this.getOfflinePlayersWithStats() .filter(id -> tmm.getLastTeam(id) == competitor) .collect(Collectors.toSet()); - items.add( - new TeamStatsInventoryMenuItem( - match, competitor, relevantObservers, relevantOfflinePlayers)); + teams.add( + new TeamStatsMenuItem(match, competitor, relevantObservers, relevantOfflinePlayers)); } - this.teamItems = items; } - List items = new ArrayList<>(this.teamItems); - - // Add the player item in the middle - items.add( - (items.size() - 1) / 2 + 1, - new PlayerStatsInventoryMenuItem( - player.getId(), - this.getPlayerStat(player), - NMSHacks.getPlayerSkin(player.getBukkit()), - player.getNameLegacy(), - player.getParty().getName().color())); - - final InventoryMenu menu = - new InventoryMenu( - match.getWorld(), - verboseStatsTitle, - items, - competitors.size() <= 5 ? new SingleRowMenuArranger() : new DoubleRowMenuArranger()); - - player - .getInventory() - .setItem(7, new VerboseStatsInventoryMenuItem(menu).createItem(player.getBukkit())); - if (forceOpen) menu.display(player.getBukkit()); + StatsMainMenu menu = new StatsMainMenu(player, teams, this); + player.getInventory().setItem(7, menu.getItem()); + + if (forceOpen) { + menu.open(); + } } private Map.Entry sortStats(Map map) { diff --git a/core/src/main/java/tc/oc/pgm/stats/TeamStats.java b/core/src/main/java/tc/oc/pgm/stats/TeamStats.java new file mode 100644 index 0000000000..09e43dec0c --- /dev/null +++ b/core/src/main/java/tc/oc/pgm/stats/TeamStats.java @@ -0,0 +1,72 @@ +package tc.oc.pgm.stats; + +import tc.oc.pgm.api.party.Competitor; +import tc.oc.pgm.api.player.MatchPlayer; + +// Holds calculated total stats for a single team +public class TeamStats { + + private int teamKills = 0; + private int teamDeaths = 0; + private double damageDone = 0; + private double damageTaken = 0; + private double bowDamage = 0; + private int shotsTaken = 0; + private int shotsHit = 0; + + private double teamKD; + private double teamBowAcc; + + public TeamStats(Competitor team, StatsMatchModule statsModule) { + + for (MatchPlayer teamPlayer : team.getPlayers()) { + PlayerStats stats = statsModule.getPlayerStat(teamPlayer.getId()); + teamKills += stats.getKills(); + teamDeaths += stats.getDeaths(); + damageDone += stats.getDamageDone(); + damageTaken += stats.getDamageTaken(); + bowDamage += stats.getBowDamage(); + shotsTaken += stats.getShotsTaken(); + shotsHit += stats.getShotsHit(); + } + + teamKD = teamDeaths == 0 ? teamKills : teamKills / (double) teamDeaths; + teamBowAcc = shotsTaken == 0 ? Double.NaN : shotsHit / (shotsTaken / (double) 100); + } + + public int getTeamKills() { + return teamKills; + } + + public int getTeamDeaths() { + return teamDeaths; + } + + public double getDamageDone() { + return damageDone; + } + + public double getDamageTaken() { + return damageTaken; + } + + public double getBowDamage() { + return bowDamage; + } + + public int getShotsTaken() { + return shotsTaken; + } + + public int getShotsHit() { + return shotsHit; + } + + public double getTeamKD() { + return teamKD; + } + + public double getTeamBowAcc() { + return teamBowAcc; + } +} diff --git a/core/src/main/java/tc/oc/pgm/stats/menu/StatsMainMenu.java b/core/src/main/java/tc/oc/pgm/stats/menu/StatsMainMenu.java new file mode 100644 index 0000000000..2745bc6ac5 --- /dev/null +++ b/core/src/main/java/tc/oc/pgm/stats/menu/StatsMainMenu.java @@ -0,0 +1,106 @@ +package tc.oc.pgm.stats.menu; + +import static net.kyori.adventure.text.Component.translatable; + +import fr.minuskube.inv.ClickableItem; +import fr.minuskube.inv.content.InventoryContents; +import fr.minuskube.inv.content.SlotPos; +import java.util.List; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import tc.oc.pgm.api.player.MatchPlayer; +import tc.oc.pgm.menu.PagedInventoryMenu; +import tc.oc.pgm.stats.StatsMatchModule; +import tc.oc.pgm.stats.menu.items.TeamStatsMenuItem; +import tc.oc.pgm.stats.menu.items.VerboseStatsMenuItem; + +/** + * Menu overview of match stats - populated with {@link TeamStatsMenuItem} which lead to more + * detailed team stats * + */ +public class StatsMainMenu extends PagedInventoryMenu { + + // GUI values + private static final int TOTAL_ROWS = 4; + private static final int PER_PAGE = 18; + + // How to populate the inventory slots when within fancy slot max + private static final int MAX_FANCY_SLOTS = 13; + private static final int[][] FANCY_SLOTS = {{3, 5, 1, 7}, {4, 0, 8, 2, 6}}; + + private final StatsMatchModule stats; + private final VerboseStatsMenuItem item; + private final List teams; + + public StatsMainMenu(MatchPlayer viewer, List teams, StatsMatchModule stats) { + super( + translatable("match.stats.title", NamedTextColor.GOLD), + TOTAL_ROWS, + viewer, + null, + PER_PAGE, + 1, + 0); + this.stats = stats; + this.teams = teams; + this.item = new VerboseStatsMenuItem(); + } + + public ItemStack getItem() { + return item.createItem(getBukkit()); + } + + @Override + public void init(Player player, InventoryContents contents) { + contents.set( + 0, 4, ClickableItem.empty(stats.getPlayerStatsItem(getViewer()).createItem(getBukkit()))); + + // Use pagination when too many teams are present + if (teams.size() > MAX_FANCY_SLOTS) { + this.setupPageContents(player, contents); + return; + } + + // Fancy Slots layout supports up to 13 teams. If a map contains more than this + // menu will default to a paginated style with 18 teams per page. + int slotCol = 0; + int slotRow = 0; + int row = 1; + for (TeamStatsMenuItem team : teams) { + contents.set(row, FANCY_SLOTS[slotRow][slotCol], team.getClickableItem(player)); + + slotCol++; + if (slotCol >= FANCY_SLOTS[slotRow].length) { + slotCol = 0; + slotRow++; + row++; + } + + if (slotRow >= FANCY_SLOTS.length) { + slotRow = 0; + } + } + } + + @Override + public SlotPos getPreviousPageSlot() { + return SlotPos.of(3, 0); + } + + @Override + public SlotPos getNextPageSlot() { + return SlotPos.of(3, 8); + } + + @Override + public SlotPos getEmptyPageSlot() { + return SlotPos.of(1, 4); + } + + @Override + public ClickableItem[] getPageContents(Player viewer) { + if (teams.isEmpty() || teams == null) return null; + return teams.stream().map(team -> team.getClickableItem(viewer)).toArray(ClickableItem[]::new); + } +} diff --git a/core/src/main/java/tc/oc/pgm/stats/menu/TeamStatsMenu.java b/core/src/main/java/tc/oc/pgm/stats/menu/TeamStatsMenu.java new file mode 100644 index 0000000000..98abb333a9 --- /dev/null +++ b/core/src/main/java/tc/oc/pgm/stats/menu/TeamStatsMenu.java @@ -0,0 +1,94 @@ +package tc.oc.pgm.stats.menu; + +import static net.kyori.adventure.text.Component.translatable; + +import fr.minuskube.inv.ClickableItem; +import fr.minuskube.inv.SmartInventory; +import fr.minuskube.inv.content.InventoryContents; +import fr.minuskube.inv.content.SlotPos; +import java.util.List; +import java.util.stream.Collectors; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextDecoration; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import tc.oc.pgm.api.party.Competitor; +import tc.oc.pgm.api.player.MatchPlayer; +import tc.oc.pgm.menu.PagedInventoryMenu; +import tc.oc.pgm.stats.TeamStats; +import tc.oc.pgm.stats.menu.items.PlayerStatsMenuItem; +import tc.oc.pgm.util.text.TextFormatter; + +/** Detailed menu of a single team's stats - populated with {@link PlayerStatsMenuItem} * */ +public class TeamStatsMenu extends PagedInventoryMenu { + + private static final int TOTAL_ROWS = 6; + private static final int PER_PAGE = 9 * 3; + private static final int STARTING_ROW = 1; + private static final int STARTING_COL = 0; + + private final Competitor team; + private final TeamStats stats; + private final List members; + private ItemStack teamItem; + + public TeamStatsMenu( + Competitor team, + TeamStats stats, + List members, + MatchPlayer viewer, + ItemStack teamItem, + SmartInventory parent) { + super( + translatable("match.stats.team", TextFormatter.convert(team.getColor()), team.getName()), + TOTAL_ROWS, + viewer, + parent, + PER_PAGE, + STARTING_ROW, + STARTING_COL); + this.team = team; + this.stats = stats; + this.members = members; + this.teamItem = teamItem; + open(); + } + + public Competitor getTeam() { + return team; + } + + @Override + public void init(Player player, InventoryContents contents) { + contents.set(0, 4, ClickableItem.empty(teamItem)); + this.setupPageContents(player, contents); + this.addBackButton( + contents, + translatable("match.stats.title", NamedTextColor.GOLD, TextDecoration.BOLD), + 5, + 4); + } + + @Override + public ClickableItem[] getPageContents(Player viewer) { + List items = + members.stream().map(ps -> ps.getClickableItem(viewer)).collect(Collectors.toList()); + + return items.isEmpty() ? null : items.toArray(new ClickableItem[items.size()]); + } + + @Override + public SlotPos getPreviousPageSlot() { + return SlotPos.of(4, 0); + } + + @Override + public SlotPos getNextPageSlot() { + return SlotPos.of(4, 8); + } + + @Override + public SlotPos getEmptyPageSlot() { + return SlotPos.of(2, 4); + } +} diff --git a/core/src/main/java/tc/oc/pgm/stats/PlayerStatsInventoryMenuItem.java b/core/src/main/java/tc/oc/pgm/stats/menu/items/PlayerStatsMenuItem.java similarity index 94% rename from core/src/main/java/tc/oc/pgm/stats/PlayerStatsInventoryMenuItem.java rename to core/src/main/java/tc/oc/pgm/stats/menu/items/PlayerStatsMenuItem.java index 1190d3d374..f7bd66b6b7 100644 --- a/core/src/main/java/tc/oc/pgm/stats/PlayerStatsInventoryMenuItem.java +++ b/core/src/main/java/tc/oc/pgm/stats/menu/items/PlayerStatsMenuItem.java @@ -1,4 +1,4 @@ -package tc.oc.pgm.stats; +package tc.oc.pgm.stats.menu.items; import static net.kyori.adventure.text.Component.text; import static net.kyori.adventure.text.Component.translatable; @@ -20,14 +20,15 @@ import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.SkullMeta; -import tc.oc.pgm.util.menu.InventoryMenu; -import tc.oc.pgm.util.menu.InventoryMenuItem; +import tc.oc.pgm.menu.MenuItem; +import tc.oc.pgm.stats.PlayerStats; import tc.oc.pgm.util.nms.NMSHacks; import tc.oc.pgm.util.skin.Skin; import tc.oc.pgm.util.text.TemporalComponent; import tc.oc.pgm.util.text.TextTranslations; -public class PlayerStatsInventoryMenuItem implements InventoryMenuItem { +/** Represents a player's stats via player head & lore * */ +public class PlayerStatsMenuItem implements MenuItem { private final TextColor RESET = NamedTextColor.GRAY; private final UUID uuid; @@ -36,7 +37,7 @@ public class PlayerStatsInventoryMenuItem implements InventoryMenuItem { private final String name; private final TextColor color; - PlayerStatsInventoryMenuItem( + public PlayerStatsMenuItem( UUID uuid, PlayerStats stats, Skin skin, String name, TextColor color) { this.uuid = uuid; this.stats = stats; @@ -128,7 +129,7 @@ public Material getMaterial(Player player) { } @Override - public void onInventoryClick(InventoryMenu menu, Player player, ClickType clickType) {} + public void onClick(Player player, ClickType clickType) {} @Override public ItemMeta modifyMeta(ItemMeta meta) { diff --git a/core/src/main/java/tc/oc/pgm/stats/TeamStatsInventoryMenuItem.java b/core/src/main/java/tc/oc/pgm/stats/menu/items/TeamStatsMenuItem.java similarity index 57% rename from core/src/main/java/tc/oc/pgm/stats/TeamStatsInventoryMenuItem.java rename to core/src/main/java/tc/oc/pgm/stats/menu/items/TeamStatsMenuItem.java index 2eeef5d9ec..79a76bbcb9 100644 --- a/core/src/main/java/tc/oc/pgm/stats/TeamStatsInventoryMenuItem.java +++ b/core/src/main/java/tc/oc/pgm/stats/menu/items/TeamStatsMenuItem.java @@ -1,11 +1,11 @@ -package tc.oc.pgm.stats; +package tc.oc.pgm.stats.menu.items; import static net.kyori.adventure.text.Component.text; import static net.kyori.adventure.text.Component.translatable; import static tc.oc.pgm.stats.StatsMatchModule.damageComponent; import static tc.oc.pgm.stats.StatsMatchModule.numberComponent; -import java.util.ArrayList; +import com.google.common.collect.Lists; import java.util.Collection; import java.util.List; import java.util.UUID; @@ -23,37 +23,41 @@ import tc.oc.pgm.api.match.Match; import tc.oc.pgm.api.party.Competitor; import tc.oc.pgm.api.player.MatchPlayer; -import tc.oc.pgm.util.menu.InventoryMenu; -import tc.oc.pgm.util.menu.InventoryMenuItem; -import tc.oc.pgm.util.menu.pattern.DoubleRowMenuArranger; -import tc.oc.pgm.util.menu.pattern.IdentityMenuArranger; +import tc.oc.pgm.menu.MenuItem; +import tc.oc.pgm.stats.StatsMatchModule; +import tc.oc.pgm.stats.TeamStats; +import tc.oc.pgm.stats.menu.TeamStatsMenu; import tc.oc.pgm.util.nms.NMSHacks; import tc.oc.pgm.util.text.TextTranslations; -public class TeamStatsInventoryMenuItem implements InventoryMenuItem { +/** Represents a team with same color & lore. Clicking will open {@link TeamStatsMenu} * */ +public class TeamStatsMenuItem implements MenuItem { private final Competitor team; - private final InventoryMenu teamSubGUI; private final Match match; + private final TeamStats stats; + private List members; private final NamedTextColor RESET = NamedTextColor.GRAY; - TeamStatsInventoryMenuItem( + public TeamStatsMenuItem( Match match, Competitor team, Collection relevantObservers, Collection relevantOfflinePlayers) { + StatsMatchModule smm = match.needModule(StatsMatchModule.class); this.team = team; - StatsMatchModule smm = match.needModule(StatsMatchModule.class); + this.members = Lists.newArrayList(); + this.stats = new TeamStats(team, smm); + Collection players = team.getPlayers(); - List items = - new ArrayList<>(players.size() + relevantObservers.size() + relevantOfflinePlayers.size()); - items.addAll( + + members.addAll( Stream.concat(players.stream(), relevantObservers.stream()) .map( p -> - new PlayerStatsInventoryMenuItem( + new PlayerStatsMenuItem( p.getId(), smm.getPlayerStat(p), NMSHacks.getPlayerSkin(p.getBukkit()), @@ -62,12 +66,11 @@ public class TeamStatsInventoryMenuItem implements InventoryMenuItem { .collect(Collectors.toList())); Datastore datastore = PGM.get().getDatastore(); - - items.addAll( + members.addAll( relevantOfflinePlayers.stream() .map( id -> - new PlayerStatsInventoryMenuItem( + new PlayerStatsMenuItem( id, smm.getPlayerStat(id), datastore.getSkin(id), @@ -75,12 +78,6 @@ public class TeamStatsInventoryMenuItem implements InventoryMenuItem { NamedTextColor.DARK_AQUA)) .collect(Collectors.toSet())); - this.teamSubGUI = - new InventoryMenu( - match.getWorld(), - translatable("match.stats.title"), - items, - items.size() > 10 ? new IdentityMenuArranger(5) : new DoubleRowMenuArranger()); this.match = match; } @@ -91,54 +88,34 @@ public Component getDisplayName() { @Override public List getLore(Player player) { - - StatsMatchModule smm = match.needModule(StatsMatchModule.class); - List lore = new ArrayList<>(); - int teamKills = 0; - int teamDeaths = 0; - double damageDone = 0; - double damageTaken = 0; - double bowDamage = 0; - int shotsTaken = 0; - int shotsHit = 0; - for (MatchPlayer teamPlayer : team.getPlayers()) { - PlayerStats stats = smm.getPlayerStat(teamPlayer.getId()); - teamKills += stats.getKills(); - teamDeaths += stats.getDeaths(); - damageDone += stats.getDamageDone(); - damageTaken += stats.getDamageTaken(); - bowDamage += stats.getBowDamage(); - shotsTaken += stats.getShotsTaken(); - shotsHit += stats.getShotsHit(); - } - - double teamKD = teamDeaths == 0 ? teamKills : teamKills / (double) teamDeaths; - double teamBowAcc = shotsTaken == 0 ? Double.NaN : shotsHit / (shotsTaken / (double) 100); + List lore = Lists.newArrayList(); Component statLore = translatable( "match.stats.concise", RESET, - numberComponent(teamKills, NamedTextColor.GREEN), - numberComponent(teamDeaths, NamedTextColor.RED), - numberComponent(teamKD, NamedTextColor.GREEN)); + numberComponent(stats.getTeamKills(), NamedTextColor.GREEN), + numberComponent(stats.getTeamDeaths(), NamedTextColor.RED), + numberComponent(stats.getTeamKD(), NamedTextColor.GREEN)); Component damageDealtLore = translatable( "match.stats.damage.dealt", RESET, - damageComponent(damageDone, NamedTextColor.GREEN), - damageComponent(bowDamage, NamedTextColor.YELLOW)); + damageComponent(stats.getDamageDone(), NamedTextColor.GREEN), + damageComponent(stats.getBowDamage(), NamedTextColor.YELLOW)); Component damageReceivedLore = translatable( - "match.stats.damage.received", RESET, damageComponent(damageTaken, NamedTextColor.RED)); + "match.stats.damage.received", + RESET, + damageComponent(stats.getDamageTaken(), NamedTextColor.RED)); Component bowLore = translatable( "match.stats.bow", RESET, - numberComponent(shotsHit, NamedTextColor.YELLOW), - numberComponent(shotsTaken, NamedTextColor.YELLOW), - numberComponent(teamBowAcc, NamedTextColor.YELLOW).append(text('%'))); + numberComponent(stats.getShotsHit(), NamedTextColor.YELLOW), + numberComponent(stats.getShotsTaken(), NamedTextColor.YELLOW), + numberComponent(stats.getTeamBowAcc(), NamedTextColor.YELLOW).append(text('%'))); lore.add(TextTranslations.translateLegacy(statLore, player)); lore.add(TextTranslations.translateLegacy(damageDealtLore, player)); @@ -154,8 +131,14 @@ public Material getMaterial(Player player) { } @Override - public void onInventoryClick(InventoryMenu menu, Player player, ClickType clickType) { - teamSubGUI.display(player); + public void onClick(Player player, ClickType clickType) { + new TeamStatsMenu( + team, + stats, + members, + match.getPlayer(player), + createItem(player), + PGM.get().getInventoryManager().getInventory(player).orElse(null)); } @Override diff --git a/core/src/main/java/tc/oc/pgm/stats/VerboseStatsInventoryMenuItem.java b/core/src/main/java/tc/oc/pgm/stats/menu/items/VerboseStatsMenuItem.java similarity index 58% rename from core/src/main/java/tc/oc/pgm/stats/VerboseStatsInventoryMenuItem.java rename to core/src/main/java/tc/oc/pgm/stats/menu/items/VerboseStatsMenuItem.java index 81be7f5631..c271ceeaee 100644 --- a/core/src/main/java/tc/oc/pgm/stats/VerboseStatsInventoryMenuItem.java +++ b/core/src/main/java/tc/oc/pgm/stats/menu/items/VerboseStatsMenuItem.java @@ -1,4 +1,4 @@ -package tc.oc.pgm.stats; +package tc.oc.pgm.stats.menu.items; import static net.kyori.adventure.text.Component.translatable; @@ -10,21 +10,15 @@ import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.event.inventory.ClickType; -import tc.oc.pgm.util.menu.InventoryMenu; -import tc.oc.pgm.util.menu.InventoryMenuItem; +import tc.oc.pgm.menu.MenuItem; import tc.oc.pgm.util.text.TextTranslations; -public class VerboseStatsInventoryMenuItem implements InventoryMenuItem { - - private final InventoryMenu verboseStatsMenu; - - VerboseStatsInventoryMenuItem(InventoryMenu verboseStatsMenu) { - this.verboseStatsMenu = verboseStatsMenu; - } +/** The verbose stats menu item shown in the hotbar * */ +public class VerboseStatsMenuItem implements MenuItem { @Override public Component getDisplayName() { - return translatable("match.stats.title", NamedTextColor.GREEN, TextDecoration.BOLD); + return translatable("match.stats.title", NamedTextColor.GOLD, TextDecoration.BOLD); } @Override @@ -40,7 +34,5 @@ public Material getMaterial(Player player) { } @Override - public void onInventoryClick(InventoryMenu menu, Player player, ClickType clickType) { - verboseStatsMenu.display(player); - } + public void onClick(Player player, ClickType type) {} } diff --git a/pom.xml b/pom.xml index 5ec75d7db9..dd2b5edbb9 100644 --- a/pom.xml +++ b/pom.xml @@ -142,6 +142,14 @@ 1.1.0 compile + + + + fr.minuskube.inv + smart-invs + 1.2.7 + compile + org.junit.jupiter diff --git a/util/src/main/i18n/templates/misc.properties b/util/src/main/i18n/templates/misc.properties index 5e14855ce7..8d9760106c 100644 --- a/util/src/main/i18n/templates/misc.properties +++ b/util/src/main/i18n/templates/misc.properties @@ -197,6 +197,10 @@ misc.serverFull = Server is full, consider upgrading to premium to be able to jo misc.serverRestart = Server restarting! +misc.creative = Creative + +misc.spectator = Spectator + type.int = whole number type.float = decimal number diff --git a/util/src/main/i18n/templates/ui.properties b/util/src/main/i18n/templates/ui.properties index 36de0548e6..47f898a911 100644 --- a/util/src/main/i18n/templates/ui.properties +++ b/util/src/main/i18n/templates/ui.properties @@ -55,6 +55,86 @@ setting.options = Possible options: {0} # {2} = new setting value (e.g. "on") setting.set = Changed your {0} setting from {1} to {2} +settings.menu.title = Settings + +settings.menu.lore = Click to open settings menu + +settings.menu.toggle = Click to toggle + +settings.chat = Changes what your default chat channel is + +settings.chat.team = Chat sends messages to your team only + +settings.chat.global = Chat is sent to everyone + +settings.chat.admin = Reserved for staff + +settings.death = Changes what type of death messages are seen + +settings.death.all = View all death messages + +settings.death.own = View own death messages only + +settings.picker = Changes when the picker is displayed + +settings.picker.auto = Automatically display picker based on match + +settings.picker.on = Always display the picker + +settings.picker.off = Never display the picker + +settings.join = Changes when join messages are sent + +settings.join.on = View all join messages + +settings.join.off = Hide all join messages + +settings.message = Changes if private messages are accepted + +settings.message.on = Accept all private messages + +settings.message.off = Reject all private messages + +settings.observers = Changes if observers are visible + +settings.observers.on = Show all observers + +settings.observers.off = Hide other observers + +settings.sounds = Changes when sounds are played + +settings.sounds.all = Hear all match sounds + +settings.sounds.dm = Only hear sounds from direct messages + +settings.sounds.none = Mute all match sounds + +settings.vote = Changes if the vote book is shown on cycle + +settings.vote.on = Always show the vote book + +settings.vote.off = Never show the vote book + +settings.stats = Changes if stats are tracked + +settings.stats.on = Track and display per-match stats + +settings.stats.off = Disable and hide per-match stats + +settings.effects = Changes if particle effects are shown + +settings.effects.on = View special particle effects + +settings.effects.off = Hide special particle effects + +settings.time = Changes preferred world time + +settings.time.auto = Keep time in sync with server + +settings.time.dark = Always night time + +settings.time.light = Always day time + preview.noPotionEffects = No potion effects preview.notViewable = Player's inventory is not currently viewable @@ -113,3 +193,12 @@ picker.clickToJoin = You are able to pick your team, click to join! picker.clickToRejoin = Click to rejoin your team! picker.noPermissions = Premium users can pick their teams! + +menu.page.empty = No results found! + +menu.page.next = Next Page + +menu.page.previous = Previous Page + +# {0} = name of previous menu +menu.page.return = Return to {0} diff --git a/util/src/main/java/tc/oc/pgm/util/menu/InventoryMenu.java b/util/src/main/java/tc/oc/pgm/util/menu/InventoryMenu.java deleted file mode 100644 index 848a225d27..0000000000 --- a/util/src/main/java/tc/oc/pgm/util/menu/InventoryMenu.java +++ /dev/null @@ -1,329 +0,0 @@ -package tc.oc.pgm.util.menu; - -import static com.google.common.base.Preconditions.checkArgument; -import static net.kyori.adventure.text.Component.translatable; -import static tc.oc.pgm.util.menu.InventoryMenuUtils.howManyRows; - -import com.google.common.collect.Lists; -import java.util.ArrayList; -import java.util.List; -import java.util.WeakHashMap; -import javax.annotation.Nullable; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.format.NamedTextColor; -import org.bukkit.Bukkit; -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.HandlerList; -import org.bukkit.event.Listener; -import org.bukkit.event.inventory.ClickType; -import org.bukkit.event.inventory.InventoryClickEvent; -import org.bukkit.event.world.WorldUnloadEvent; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.ItemStack; -import tc.oc.pgm.util.StringUtils; -import tc.oc.pgm.util.bukkit.BukkitUtils; -import tc.oc.pgm.util.menu.pattern.MenuArranger; -import tc.oc.pgm.util.text.TextTranslations; - -/** - * A way to make a GUI menu that users can interact with. - * - *

The {@link MenuArranger} is what decides whether a menu will be paginated, and how the items - * will be scattered throughout the GUI. This means any list passed in the constructor of an {@link - * InventoryMenu} should only have non-{@code null} elements. - * - *

Whenever an item in this inventory is clicked it will automatically call the items {@code - * onInventoryClick} method. If that (or any) method changes something in the GUI {@link - * #refreshWindow(Player)} or {@link #refreshAll()} should be called to rebuild the GUI for the - * relevant players. - */ -public class InventoryMenu implements Listener { - - protected static final int ROW_WIDTH = 9; // Number of columns per row - protected static final int MAX_ROWS = 6; // Max allowed row size - - private final List inventoryMenuItems; - - private final WeakHashMap viewing = new WeakHashMap<>(); - - private final Component title; // Title of the inventory - private int rows; // The # of rows in the inventory - private final World world; // The world this inventory exists in; - - /** - * InventoryMenu: An easy way to make an GUI menu that users can interact with. - * - *

Note: Code here was initially extracted from PickerMatchModule to allow for reuse - * - * @param world - The world this inventory should exist it - * @param title - the inventory title - * @param items - The items this inventory will contain, null counts as spaces - * @param menuArranger arranges the items in different ways while also assisting pagination, is - * null when called by page items - * @param rows - The maximum amount of rows the menu can have - * @param pagesPossible internal boolean used to prevent infinite pages - */ - private InventoryMenu( - World world, - Component title, - List items, - @Nullable MenuArranger menuArranger, - int rows, - boolean pagesPossible) { - this.title = title; - this.rows = rows; - this.inventoryMenuItems = applyPatternAndAddPages(items, menuArranger, pagesPossible); - // This argument check needs to be under the applyPattern call because it can increase the row - // number(pagination) - checkArgument(rows > 0 && rows <= MAX_ROWS, "Row size must be between 1 and " + MAX_ROWS); - this.world = world; - enableInventory(); - } - - public InventoryMenu( - World world, - Component title, - List items, - MenuArranger menuArranger, - int rows) { - this(world, title, items, menuArranger, rows, true); - } - - public InventoryMenu( - World world, Component title, List items, MenuArranger menuArranger) { - this(world, title, items, menuArranger, menuArranger.rows(), true); - } - - private InventoryMenu(World world, Component title, List items) { - this(world, title, items, null, howManyRows(items), false); - } - - public ItemStack[] createWindowContents(final Player player) { - List items = Lists.newArrayList(); - for (InventoryMenuItem item : this.inventoryMenuItems) { - if (item == null) items.add(null); - else items.add(item.createItem(player)); - } - - return items.toArray(new ItemStack[0]); - } - - public String getTranslatedTitle(Player player) { - return TextTranslations.translateLegacy(title, player); - } - - public boolean isViewing(Player player) { - return viewing.containsKey(player); - } - - public void display(Player player) { - this.showWindow(player); - this.viewing.put(player, true); - } - - public boolean remove(Player player) { - return this.viewing.remove(player); - } - - public void refreshAll() { - viewing.keySet().forEach(this::refreshWindow); - } - - private int getInventorySize() { - return ROW_WIDTH * rows; - } - - /** - * Open the window for the given player, or refresh its contents if they already have it open, and - * return the current contents. - * - *

If the window is currently open but too small to hold the current contents, it will be - * closed and reopened. - * - *

If the player is not currently allowed to have the window open, close any window they have - * open and return null. - */ - private Inventory showWindow(Player player) { - ItemStack[] contents = createWindowContents(player); - Inventory inv = getOpenWindow(player); - if (inv != null && inv.getSize() < contents.length) { - inv = null; - closeWindow(player); - } - if (inv == null) { - inv = openWindow(player, contents); - } else { - inv.setContents(contents); - } - return inv; - } - - /** - * If the given player currently has the window open, refresh its contents and return the updated - * inventory. The window will be closed and reopened if it is too small to hold the current - * contents. - * - *

If the window is open but should be closed, close it and return null. - * - *

If the player does not have the window open, return null. - */ - public @Nullable Inventory refreshWindow(Player player) { - Inventory inv = getOpenWindow(player); - if (inv != null) { - ItemStack[] contents = createWindowContents(player); - if (inv.getSize() < contents.length) { - closeWindow(player); - inv = openWindow(player, contents); - } else { - inv.setContents(contents); - } - } - return inv; - } - - /** - * Return the inventory of the given player's currently open window, or null if the player does - * not have the window open. - */ - private @Nullable Inventory getOpenWindow(Player player) { - if (isViewing(player)) { - return player.getOpenInventory().getTopInventory(); - } - return null; - } - - /** Close any window that is currently open for the given player */ - private void closeWindow(Player player) { - if (isViewing(player)) { - player.closeInventory(); - } - } - - /** Open a new window for the given player displaying the given contents */ - private Inventory openWindow(Player player, ItemStack[] contents) { - closeWindow(player); - Inventory inv = - Bukkit.createInventory( - player, getInventorySize(), StringUtils.truncate(getTranslatedTitle(player), 32)); - - inv.setContents(contents); - player.openInventory(inv); - viewing.put(player, true); - return inv; - } - - @EventHandler - public void onInventoryClick(final InventoryClickEvent event) { - if (inventoryMenuItems == null - || event.getInventory() == null - || event.getInventory().getHolder() == null - || event.getCurrentItem() == null - || event.getCurrentItem().getItemMeta() == null - || event.getCurrentItem().getItemMeta().getDisplayName() == null) return; - - if (event.getWhoClicked() instanceof Player) { - Player player = ((Player) event.getWhoClicked()); - if (player.getWorld() == this.world && isViewing(player)) { - ItemStack clicked = event.getCurrentItem(); - for (InventoryMenuItem item : this.inventoryMenuItems) { - if (item == null) continue; - - if (clicked.equals(item.createItem(player))) { - item.onInventoryClick(this, player, event.getClick()); - } - } - } - } - } - - @EventHandler - public void onWorldUnload(WorldUnloadEvent event) { - if (this.world != event.getWorld()) return; - disableInventory(); - } - - public void disableInventory() { - viewing.keySet().forEach(this::closeWindow); - viewing.clear(); - HandlerList.unregisterAll(this); - } - - public void enableInventory() { - Bukkit.getPluginManager().registerEvents(this, BukkitUtils.getPlugin()); - } - - private List applyPatternAndAddPages( - List items, MenuArranger menuArranger, boolean pagesPossible) { - - List mutableItems = new ArrayList<>(items); - if (!pagesPossible) return items; - - // Quick exit if we dont need any pages - if (menuArranger.automatedPaginationLimit() > mutableItems.size()) - return menuArranger.arrangeItems(mutableItems); - - // We need pages!! - rows++; - - List> pages = new ArrayList<>(); - - // Put items into pages - List page = new ArrayList<>(); - for (int i = 0; !mutableItems.isEmpty(); i++) { - page.add(mutableItems.remove(0)); - if (i + 1 == menuArranger.automatedPaginationLimit() || mutableItems.isEmpty()) { // new page - pages.add(new ArrayList<>(menuArranger.arrangeItems(page))); - page.clear(); - i = 0; - } - } - - // Insert pagination items on every page - for (int i = 0; i < pages.size(); i++) { - List currentPage = pages.get(i); - for (int item = 0; item < ROW_WIDTH; item++) currentPage.add(null); - if (i > 0) // Is there a previous page? - currentPage.set( - 2 + (ROW_WIDTH * (rows - 1)), new PageInventoryMenuItem(pages.get(i - 1), false)); - - if (i < pages.size() - 1) // Is there a next page? - currentPage.set( - 6 + (ROW_WIDTH * (rows - 1)), new PageInventoryMenuItem(pages.get(i + 1), true)); - } - - return pages.get(0); - } - - private class PageInventoryMenuItem implements InventoryMenuItem { - private final List inventoryMenu; - private final boolean next; // Does this represent the next page - - PageInventoryMenuItem(List items, boolean next) { - this.inventoryMenu = items; - this.next = next; - } - - @Override - public Component getDisplayName() { - return translatable(next ? "misc.nextPage" : "misc.previousPage", NamedTextColor.WHITE); - } - - @Override - public List getLore(Player player) { - return null; - } - - @Override - public Material getMaterial(Player player) { - return Material.ARROW; - } - - @Override - public void onInventoryClick(InventoryMenu menu, Player player, ClickType clickType) { - new InventoryMenu(world, title, inventoryMenu).display(player); - } - } -} diff --git a/util/src/main/java/tc/oc/pgm/util/menu/InventoryMenuUtils.java b/util/src/main/java/tc/oc/pgm/util/menu/InventoryMenuUtils.java deleted file mode 100644 index ea582b252f..0000000000 --- a/util/src/main/java/tc/oc/pgm/util/menu/InventoryMenuUtils.java +++ /dev/null @@ -1,65 +0,0 @@ -package tc.oc.pgm.util.menu; - -import java.util.ArrayList; -import java.util.List; - -/** A collection of some static methods for building {@link InventoryMenu}s */ -public class InventoryMenuUtils { - - /** - * Simulates an empty row in a list that will be used to create a {@link InventoryMenu} (9 empty - * slots) - * - * @param content The list you want an empty row in - */ - public static List putEmptyRow(List content) { - for (int i = 0; i < 9; i++) { - content.add(null); - } - return content; - } - - public static List emptyRow() { - List row = new ArrayList<>(); - return putEmptyRow(row); - } - - // Puts the given items in the given slots - public static List itemsInSlots( - List items, int[] slots, int rows) { - if (items.isEmpty()) return emptyRow(); - List inventory = new ArrayList<>(); - - int highestSlot = rows * InventoryMenu.ROW_WIDTH; - - for (int i = 0, j = 0; i < highestSlot; i++) { - boolean placeItem = false; - - for (int slot : slots) { - if (i == slot) { - placeItem = true; - break; - } - } - - if (placeItem) { - inventory.add(items.get(j)); - j++; - } else inventory.add(null); - } - - return inventory; - } - - // Figures out the smallest amount of required rows for the given menu - static int howManyRows(List menu) { - int rows = 0; - int size = menu.size(); - do { - rows++; - size -= 9; - } while (size > 0); - - return rows; - } -} diff --git a/util/src/main/java/tc/oc/pgm/util/menu/pattern/DoubleRowMenuArranger.java b/util/src/main/java/tc/oc/pgm/util/menu/pattern/DoubleRowMenuArranger.java deleted file mode 100644 index 70c184f45a..0000000000 --- a/util/src/main/java/tc/oc/pgm/util/menu/pattern/DoubleRowMenuArranger.java +++ /dev/null @@ -1,41 +0,0 @@ -package tc.oc.pgm.util.menu.pattern; - -import static tc.oc.pgm.util.menu.InventoryMenuUtils.emptyRow; - -import java.util.ArrayList; -import java.util.List; -import tc.oc.pgm.util.menu.InventoryMenuItem; - -// Includes a space, so technically three rows -/** Fits 0-10 items */ -public class DoubleRowMenuArranger extends MenuArranger { - - @Override - public List arrangeItems(List itemsWithoutSpaces) { - - List itemsWithSpaces = new ArrayList<>(27); - - int size = itemsWithoutSpaces.size(); - - SingleRowMenuArranger arranger = new SingleRowMenuArranger(); - - itemsWithSpaces.addAll(arranger.arrangeItems(itemsWithoutSpaces.subList(0, Math.min(size, 5)))); - itemsWithSpaces.addAll(emptyRow()); - if (size >= 6) - itemsWithSpaces.addAll( - arranger.arrangeItems(itemsWithoutSpaces.subList(5, Math.min(size, 10)))); - else itemsWithSpaces.addAll(emptyRow()); - - return itemsWithSpaces; - } - - @Override - public int rows() { - return 3; - } - - @Override // Only fits 10 items per screen - public int automatedPaginationLimit() { - return 10; - } -} diff --git a/util/src/main/java/tc/oc/pgm/util/menu/pattern/IdentityMenuArranger.java b/util/src/main/java/tc/oc/pgm/util/menu/pattern/IdentityMenuArranger.java deleted file mode 100644 index 4bc1c219d6..0000000000 --- a/util/src/main/java/tc/oc/pgm/util/menu/pattern/IdentityMenuArranger.java +++ /dev/null @@ -1,31 +0,0 @@ -package tc.oc.pgm.util.menu.pattern; - -import java.util.List; -import tc.oc.pgm.util.menu.InventoryMenuItem; - -// Does nothing -public class IdentityMenuArranger extends MenuArranger { - - private final int rows; - private final int paginationLimit; - - public IdentityMenuArranger(int rows) { - this.rows = rows; - this.paginationLimit = rows * 9; - } - - @Override - public List arrangeItems(List itemsWithoutSpaces) { - return itemsWithoutSpaces; - } - - @Override - public int rows() { - return rows; - } - - @Override - public int automatedPaginationLimit() { - return paginationLimit; - } -} diff --git a/util/src/main/java/tc/oc/pgm/util/menu/pattern/MenuArranger.java b/util/src/main/java/tc/oc/pgm/util/menu/pattern/MenuArranger.java deleted file mode 100644 index 6b416ba0fb..0000000000 --- a/util/src/main/java/tc/oc/pgm/util/menu/pattern/MenuArranger.java +++ /dev/null @@ -1,23 +0,0 @@ -package tc.oc.pgm.util.menu.pattern; - -import java.util.List; -import tc.oc.pgm.util.menu.InventoryMenuItem; - -// Figures out how to place items in an inventory -public abstract class MenuArranger { - - public abstract List arrangeItems(List itemsWithoutSpaces); - - /** - * How many rows does the inventory need to fit the items arranged by this - * - * @return the amount of rows - */ - public abstract int rows(); - - // If the items surpass this limit a pagination row will be added to the inventory using this - // arranger - public int automatedPaginationLimit() { - return Integer.MAX_VALUE; - } -} diff --git a/util/src/main/java/tc/oc/pgm/util/menu/pattern/SingleRowMenuArranger.java b/util/src/main/java/tc/oc/pgm/util/menu/pattern/SingleRowMenuArranger.java deleted file mode 100644 index 79f2bf881e..0000000000 --- a/util/src/main/java/tc/oc/pgm/util/menu/pattern/SingleRowMenuArranger.java +++ /dev/null @@ -1,35 +0,0 @@ -package tc.oc.pgm.util.menu.pattern; - -import java.util.ArrayList; -import java.util.List; -import tc.oc.pgm.util.menu.InventoryMenuItem; - -/** Fits 0-4 items, never paginates */ -public class SingleRowMenuArranger extends MenuArranger { - - @Override - public List arrangeItems(List itemsWithoutSpaces) { - final List itemsWithSpaces = new ArrayList<>(); - - final int s = itemsWithoutSpaces.size(); - - itemsWithSpaces.add(s >= 5 ? itemsWithoutSpaces.get(0) : null); - itemsWithSpaces.add(s == 4 ? itemsWithoutSpaces.get(0) : null); - itemsWithSpaces.add(s == 2 || s == 3 || s >= 5 ? itemsWithoutSpaces.get(s >= 5 ? 1 : 0) : null); - itemsWithSpaces.add(s == 4 ? itemsWithoutSpaces.get(1) : null); - itemsWithSpaces.add( - s == 1 || s == 3 || s >= 5 ? itemsWithoutSpaces.get(s == 3 ? 1 : s >= 5 ? 2 : 0) : null); - itemsWithSpaces.add(s == 4 ? itemsWithoutSpaces.get(2) : null); - itemsWithSpaces.add( - s == 2 || s == 3 || s >= 5 ? itemsWithoutSpaces.get(s == 3 ? 2 : s >= 5 ? 3 : 1) : null); - itemsWithSpaces.add(s == 4 ? itemsWithoutSpaces.get(3) : null); - itemsWithSpaces.add(s >= 5 ? itemsWithoutSpaces.get(4) : null); - - return itemsWithSpaces; - } - - @Override - public int rows() { - return 1; - } -} From f49c568ccda84f9c0e8aae44c71b745b0dd3ae1f Mon Sep 17 00:00:00 2001 From: applenick Date: Sat, 28 Aug 2021 12:30:21 -0700 Subject: [PATCH 2/3] Add spigot repo to resolve build issues Signed-off-by: applenick --- pom.xml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index dd2b5edbb9..20d154b9cd 100644 --- a/pom.xml +++ b/pom.xml @@ -51,11 +51,15 @@ ashcon.app https://repo.ashcon.app/content/repositories/snapshots - + sonatype-oss https://oss.sonatype.org/content/repositories/snapshots + + spigot-repo + https://hub.spigotmc.org/nexus/content/repositories/snapshots/ + From 232ad40bce697ce98aebcd6839d859609896e21a Mon Sep 17 00:00:00 2001 From: applenick Date: Thu, 2 Sep 2021 18:04:38 -0700 Subject: [PATCH 3/3] Fix formatting issue on pom.xml Signed-off-by: applenick --- pom.xml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/pom.xml b/pom.xml index 20d154b9cd..b5475b3580 100644 --- a/pom.xml +++ b/pom.xml @@ -57,9 +57,9 @@ https://oss.sonatype.org/content/repositories/snapshots - spigot-repo - https://hub.spigotmc.org/nexus/content/repositories/snapshots/ - + spigot-repo + https://hub.spigotmc.org/nexus/content/repositories/snapshots/ + @@ -148,12 +148,12 @@ - - fr.minuskube.inv - smart-invs - 1.2.7 - compile - + + fr.minuskube.inv + smart-invs + 1.2.7 + compile + org.junit.jupiter