From a414a5ff08c17cdc160f008f7a487a05e4fe1664 Mon Sep 17 00:00:00 2001 From: AppleNick Date: Tue, 28 Jan 2020 17:21:31 -0800 Subject: [PATCH] Add Observer Tools --- src/main/i18n/templates/strings.properties | 38 ++++ src/main/java/tc/oc/pgm/PGMImpl.java | 3 + .../tc/oc/pgm/api/player/MatchPlayer.java | 15 ++ .../tc/oc/pgm/commands/ObserverCommands.java | 33 ++++ src/main/java/tc/oc/pgm/kits/GameModeKit.java | 2 +- .../java/tc/oc/pgm/match/MatchPlayerImpl.java | 12 +- .../java/tc/oc/pgm/menu/InventoryMenu.java | 151 ++++++++++++++ .../tc/oc/pgm/menu/InventoryMenuItem.java | 40 ++++ .../observers/ObserverToolsMatchModule.java | 187 ++++++++++++++++++ .../oc/pgm/observers/tools/FlySpeedTool.java | 93 +++++++++ .../oc/pgm/observers/tools/GamemodeTool.java | 96 +++++++++ .../pgm/observers/tools/NightVisionTool.java | 66 +++++++ .../pgm/observers/tools/VisibilityTool.java | 64 ++++++ 13 files changed, 798 insertions(+), 2 deletions(-) create mode 100644 src/main/java/tc/oc/pgm/commands/ObserverCommands.java create mode 100644 src/main/java/tc/oc/pgm/menu/InventoryMenu.java create mode 100644 src/main/java/tc/oc/pgm/menu/InventoryMenuItem.java create mode 100644 src/main/java/tc/oc/pgm/observers/ObserverToolsMatchModule.java create mode 100644 src/main/java/tc/oc/pgm/observers/tools/FlySpeedTool.java create mode 100644 src/main/java/tc/oc/pgm/observers/tools/GamemodeTool.java create mode 100644 src/main/java/tc/oc/pgm/observers/tools/NightVisionTool.java create mode 100644 src/main/java/tc/oc/pgm/observers/tools/VisibilityTool.java diff --git a/src/main/i18n/templates/strings.properties b/src/main/i18n/templates/strings.properties index 7ed8b327b8..b72638af69 100644 --- a/src/main/i18n/templates/strings.properties +++ b/src/main/i18n/templates/strings.properties @@ -850,7 +850,45 @@ misc.seconds.singularCompound = {0} second # {0} = number of seconds misc.seconds.pluralCompound = {0} seconds +misc.on = On +misc.off = Off + # {0} = the page # {1} = the total pages pageHeader = Page {0} of {1} currentPage = Page {0} + +observer.tools.title = Observer Tools +observer.tools.displayName = Tools +observer.tools.lore = Right-click to open the tool menu. + +observer.tools.flyspeed = Fly Speed +observer.tools.nightvision = Night Vision +observer.tools.gamemode = Gamemode +observer.tools.visibility = Visibility + +# {0} = the player's speed +observer.tools.flyspeed.lore = Current Speed: {0} + +# {0} = the name of a gamemode +observer.tools.gamemode.lore = Current Gamemode: {0} + +# {0} = the status (on/off) +observer.tools.nightvision.lore = Status: {0} + +# {0} = the status of visibility setting +observer.tools.visibility.lore = Observer Visibility: {0} + +# {0} = the command name +observer.tools.gamemode.warning = To toggle your gamemode back, use {0} +observer.tools.gamemode.hover = Click to open tool menu + +observer.command.wrongmode = Sorry, this command is only for observers. + +observer.tools.flyspeed.normal = Normal +observer.tools.flyspeed.fast = Fast +observer.tools.flyspeed.faster = Faster +observer.tools.flyspeed.hyperspeed = Hyperspeed + +observer.tools.visibility.shown = Shown +observer.tools.visibility.hidden = Hidden diff --git a/src/main/java/tc/oc/pgm/PGMImpl.java b/src/main/java/tc/oc/pgm/PGMImpl.java index cf71dcd941..df9e6af70b 100644 --- a/src/main/java/tc/oc/pgm/PGMImpl.java +++ b/src/main/java/tc/oc/pgm/PGMImpl.java @@ -114,6 +114,7 @@ import tc.oc.pgm.modules.SoundsMatchModule; import tc.oc.pgm.modules.TimeLockModule; import tc.oc.pgm.modules.ToolRepairModule; +import tc.oc.pgm.observers.ObserverToolsMatchModule; import tc.oc.pgm.picker.PickerModule; import tc.oc.pgm.portals.PortalModule; import tc.oc.pgm.prefix.PrefixRegistry; @@ -422,6 +423,7 @@ private ModuleRegistry createPGMModuleFactory() throws Throwable { factory.register(JoinMatchModule.class, new JoinMatchModule.Factory()); factory.register(CycleMatchModule.class, new CycleMatchModule.Factory()); factory.register(SoundsMatchModule.class, new SoundsMatchModule.Factory()); + factory.register(ObserverToolsMatchModule.class, new ObserverToolsMatchModule.Factory()); return factory; } @@ -495,6 +497,7 @@ private void registerCommands() { node.registerCommands(new MapPoolCommands()); node.registerCommands(new SettingCommands()); node.registerCommands(new ModerationCommands()); + node.registerCommands(new ObserverCommands()); new BukkitIntake(this, graph).register(); } diff --git a/src/main/java/tc/oc/pgm/api/player/MatchPlayer.java b/src/main/java/tc/oc/pgm/api/player/MatchPlayer.java index 1ef4a13efe..5737648905 100644 --- a/src/main/java/tc/oc/pgm/api/player/MatchPlayer.java +++ b/src/main/java/tc/oc/pgm/api/player/MatchPlayer.java @@ -2,6 +2,7 @@ import java.util.UUID; import javax.annotation.Nullable; +import org.bukkit.GameMode; import org.bukkit.entity.Player; import org.bukkit.inventory.InventoryHolder; import org.bukkit.inventory.PlayerInventory; @@ -194,6 +195,13 @@ public interface MatchPlayer extends Audience, Named, Tickable, InventoryHolder */ void applyKit(Kit kit, boolean force); + /** + * Set the {@link MatchPlayer}'s {@link GameMode}. + * + * @param gameMode - The gamemode to set + */ + void setGameMode(GameMode gameMode); + /** * Get the protocol version of the {@link MatchPlayer}'s client * @@ -210,6 +218,13 @@ public interface MatchPlayer extends Audience, Named, Tickable, InventoryHolder String getPrefixedName(); + /** + * Get the {@link GameMode} of the {@link MatchPlayer}. + * + * @return the current {@link GameMode} + */ + GameMode getGameMode(); + @Override PlayerInventory getInventory(); diff --git a/src/main/java/tc/oc/pgm/commands/ObserverCommands.java b/src/main/java/tc/oc/pgm/commands/ObserverCommands.java new file mode 100644 index 0000000000..d9838b75e0 --- /dev/null +++ b/src/main/java/tc/oc/pgm/commands/ObserverCommands.java @@ -0,0 +1,33 @@ +package tc.oc.pgm.commands; + +import app.ashcon.intake.Command; +import app.ashcon.intake.CommandException; +import java.util.Optional; +import org.bukkit.command.CommandSender; +import tc.oc.component.render.ComponentRenderers; +import tc.oc.component.types.PersonalizedTranslatable; +import tc.oc.pgm.api.player.MatchPlayer; +import tc.oc.pgm.observers.ObserverToolsMatchModule; + +public class ObserverCommands { + + @Command( + aliases = {"tools", "observertools", "ot"}, + desc = "Open the observer tool menu") + public static void openObserverToolMenu(CommandSender sender, MatchPlayer player) + throws CommandException { + if (player.isObserving()) { + Optional tools = + player.getMatch().getModule(ObserverToolsMatchModule.class); + + tools.ifPresent( + ot -> { + ot.openMenu(player); + }); + } else { + throw new CommandException( + ComponentRenderers.toLegacyText( + new PersonalizedTranslatable("observer.command.wrongmode"), sender)); + } + } +} diff --git a/src/main/java/tc/oc/pgm/kits/GameModeKit.java b/src/main/java/tc/oc/pgm/kits/GameModeKit.java index eff561954b..bdf947420c 100644 --- a/src/main/java/tc/oc/pgm/kits/GameModeKit.java +++ b/src/main/java/tc/oc/pgm/kits/GameModeKit.java @@ -15,6 +15,6 @@ public GameModeKit(GameMode mode) { @Override protected void applyPostEvent(MatchPlayer player, boolean force, List displacedItems) { - player.getBukkit().setGameMode(gameMode); + player.setGameMode(gameMode); } } diff --git a/src/main/java/tc/oc/pgm/match/MatchPlayerImpl.java b/src/main/java/tc/oc/pgm/match/MatchPlayerImpl.java index 2ac8351bee..efc8a7427a 100644 --- a/src/main/java/tc/oc/pgm/match/MatchPlayerImpl.java +++ b/src/main/java/tc/oc/pgm/match/MatchPlayerImpl.java @@ -191,7 +191,7 @@ public void resetGamemode() { logger.fine("Refreshing gamemode as " + (participating ? "participant" : "observer")); if (!participating) getBukkit().leaveVehicle(); - this.getBukkit().setGameMode(participating ? GameMode.SURVIVAL : GameMode.CREATIVE); + setGameMode(participating ? GameMode.SURVIVAL : GameMode.CREATIVE); this.getBukkit().setAllowFlight(!participating); this.getBukkit().spigot().setAffectsSpawning(participating); this.getBukkit().spigot().setCollidesWithEntities(participating); @@ -299,6 +299,11 @@ public void setFrozen(boolean yes) { } } + @Override + public void setGameMode(GameMode gameMode) { + getBukkit().setGameMode(gameMode); + } + /** * When max health is lowered by an item attribute or potion effect, the client can go into an * inconsistent state that has strange effects, like the death animation playing when the player @@ -370,6 +375,11 @@ public String getPrefixedName() { return PGM.get().getPrefixRegistry().getPrefixedName(getBukkit(), getParty()); } + @Override + public GameMode getGameMode() { + return getBukkit().getGameMode(); + } + @Override public void tick(Match match, Tick tick) { final Player bukkit = getBukkit(); diff --git a/src/main/java/tc/oc/pgm/menu/InventoryMenu.java b/src/main/java/tc/oc/pgm/menu/InventoryMenu.java new file mode 100644 index 0000000000..d0acee77aa --- /dev/null +++ b/src/main/java/tc/oc/pgm/menu/InventoryMenu.java @@ -0,0 +1,151 @@ +package tc.oc.pgm.menu; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import java.util.WeakHashMap; +import javax.annotation.Nullable; +import org.bukkit.Bukkit; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import tc.oc.component.render.ComponentRenderers; +import tc.oc.component.types.PersonalizedTranslatable; +import tc.oc.pgm.api.player.MatchPlayer; +import tc.oc.pgm.observers.ObserverToolsMatchModule; +import tc.oc.util.StringUtils; + +public abstract class InventoryMenu { + + 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 WeakHashMap viewing = + new WeakHashMap<>(); // Map of players who are viewing the gui, along with the menu + private final String title; // Title of the inventory + private final int rows; // The # of rows in the inventory + + /** + * InventoryMenu: An easy way to make an GUI menu that users can interact with. + * + *

See {@link ObserverToolsMatchModule} for an example on implementation + * + *

Note: Code here was extracted from PickerMatchModule to allow for reuse + * + * @param title - A string that will be translated and made the inventory title + * @param rows - The amount of rows the inventory will be created with + */ + public InventoryMenu(String title, int rows) { + checkArgument(rows > 0 && rows <= MAX_ROWS, "Row size must be between 1 and " + MAX_ROWS); + this.title = checkNotNull(title); + this.rows = rows; + } + + /** Defines how the GUI will display the layout */ + public abstract ItemStack[] createWindowContents(final MatchPlayer player); + + public String getTranslatedTitle(MatchPlayer player) { + return ComponentRenderers.toLegacyText(new PersonalizedTranslatable(title), player.getBukkit()); + } + + public boolean isViewing(MatchPlayer player) { + return viewing.containsKey(player); + } + + public void display(MatchPlayer player) { + this.showWindow(player); + this.viewing.put(player, this); + } + + public void remove(MatchPlayer player) { + 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 @Nullable Inventory showWindow(MatchPlayer 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(MatchPlayer 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(MatchPlayer player) { + if (isViewing(player)) { + return player.getBukkit().getOpenInventory().getTopInventory(); + } + return null; + } + + /** Close any window that is currently open for the given player */ + private void closeWindow(MatchPlayer player) { + if (isViewing(player)) { + player.getBukkit().closeInventory(); + } + } + + /** Open a new window for the given player displaying the given contents */ + private Inventory openWindow(MatchPlayer player, ItemStack[] contents) { + closeWindow(player); + Inventory inv = + Bukkit.createInventory( + player.getBukkit(), + getInventorySize(), + StringUtils.truncate(getTranslatedTitle(player), 32)); + + inv.setContents(contents); + player.getBukkit().openInventory(inv); + viewing.put(player, this); + return inv; + } +} diff --git a/src/main/java/tc/oc/pgm/menu/InventoryMenuItem.java b/src/main/java/tc/oc/pgm/menu/InventoryMenuItem.java new file mode 100644 index 0000000000..871fa803aa --- /dev/null +++ b/src/main/java/tc/oc/pgm/menu/InventoryMenuItem.java @@ -0,0 +1,40 @@ +package tc.oc.pgm.menu; + +import java.util.List; +import net.md_5.bungee.api.ChatColor; +import org.bukkit.Material; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import tc.oc.component.Component; +import tc.oc.component.render.ComponentRenderers; +import tc.oc.pgm.api.player.MatchPlayer; + +public interface InventoryMenuItem { + + public Component getName(); + + public ChatColor getColor(); + + public List getLore(MatchPlayer player); + + public Material getMaterial(MatchPlayer player); + + public void onInventoryClick(InventoryMenu menu, MatchPlayer player); + + default ItemStack createItem(MatchPlayer player) { + ItemStack stack = new ItemStack(getMaterial(player)); + ItemMeta meta = stack.getItemMeta(); + + meta.setDisplayName( + getColor() + + ChatColor.BOLD.toString() + + ComponentRenderers.toLegacyText(getName(), player.getBukkit())); + meta.setLore(getLore(player)); + meta.addItemFlags(ItemFlag.values()); + + stack.setItemMeta(meta); + + return stack; + } +} diff --git a/src/main/java/tc/oc/pgm/observers/ObserverToolsMatchModule.java b/src/main/java/tc/oc/pgm/observers/ObserverToolsMatchModule.java new file mode 100644 index 0000000000..5d3cddcf53 --- /dev/null +++ b/src/main/java/tc/oc/pgm/observers/ObserverToolsMatchModule.java @@ -0,0 +1,187 @@ +package tc.oc.pgm.observers; + +import com.google.common.collect.Lists; +import java.util.List; +import net.md_5.bungee.api.ChatColor; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import tc.oc.component.Component; +import tc.oc.component.render.ComponentRenderers; +import tc.oc.component.types.PersonalizedTranslatable; +import tc.oc.pgm.api.match.Match; +import tc.oc.pgm.api.match.MatchScope; +import tc.oc.pgm.api.player.MatchPlayer; +import tc.oc.pgm.events.ListenerScope; +import tc.oc.pgm.match.MatchModule; +import tc.oc.pgm.match.MatchModuleFactory; +import tc.oc.pgm.menu.InventoryMenu; +import tc.oc.pgm.menu.InventoryMenuItem; +import tc.oc.pgm.module.ModuleLoadException; +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; + +@ListenerScope(MatchScope.LOADED) +public class ObserverToolsMatchModule extends MatchModule implements Listener { + + public static class Factory implements MatchModuleFactory { + @Override + public ObserverToolsMatchModule createMatchModule(Match match) throws ModuleLoadException { + return new ObserverToolsMatchModule(match); + } + } + + // Slot where tool item is placed + public static final int TOOL_BUTTON_SLOT = 8; + + // Material of tool item item + public static final Material TOOL_MATERIAL = Material.DIAMOND; + + private ObserverToolMenu menu; + + public ObserverToolsMatchModule(Match match) { + super(match); + this.menu = new ObserverToolMenu(); + } + + @EventHandler + public void onObserverKitApply(ObserverKitApplyEvent event) { + refreshKit(event.getPlayer()); + } + + @EventHandler + public void onToolClick(PlayerInteractEvent event) { + if (isRightClick(event.getAction())) { + ItemStack item = event.getPlayer().getItemInHand(); + + if (item.getType().equals(TOOL_MATERIAL)) { + MatchPlayer player = getMatch().getPlayer(event.getPlayer()); + openMenu(player); + } + } + } + + @EventHandler(priority = EventPriority.LOWEST) + public void onInventoryClick(final InventoryClickEvent event) { + if (event.getCurrentItem() == null + || event.getCurrentItem().getItemMeta() == null + || event.getCurrentItem().getItemMeta().getDisplayName() == null) return; + + if (event.getWhoClicked() instanceof Player) { + MatchPlayer player = match.getPlayer(event.getWhoClicked()); + if (menu.isViewing(player)) { + ItemStack clicked = event.getCurrentItem(); + menu.getTools() + .forEach( + tool -> { + if (clicked.getType().equals(tool.getMaterial(player))) { + tool.onInventoryClick(menu, player); + } + }); + } + } + } + + @EventHandler + public void onInventoryClose(final InventoryCloseEvent event) { + // Remove viewing of menu upon inventory close + menu.remove(getMatch().getPlayer((Player) event.getPlayer())); + } + + public void openMenu(MatchPlayer player) { + if (canUse(player)) { + menu.display(player); + } + } + + private boolean canUse(MatchPlayer player) { + return player.isObserving(); + } + + private void refreshKit(MatchPlayer player) { + if (canUse(player)) { + player.getInventory().setItem(TOOL_BUTTON_SLOT, createToolItem(player)); + } + } + + private boolean isRightClick(Action action) { + return action == Action.RIGHT_CLICK_AIR || action == Action.RIGHT_CLICK_BLOCK; + } + + private ItemStack createToolItem(MatchPlayer player) { + ItemStack tool = new ItemStack(TOOL_MATERIAL); + ItemMeta meta = tool.getItemMeta(); + Component displayName = + new PersonalizedTranslatable("observer.tools.displayName") + .getPersonalizedText() + .color(ChatColor.AQUA) + .bold(true); + Component lore = + new PersonalizedTranslatable("observer.tools.lore") + .getPersonalizedText() + .color(ChatColor.GRAY); + meta.setDisplayName(ComponentRenderers.toLegacyText(displayName, player.getBukkit())); + meta.setLore(Lists.newArrayList(ComponentRenderers.toLegacyText(lore, player.getBukkit()))); + meta.addItemFlags(ItemFlag.values()); + tool.setItemMeta(meta); + return tool; + } + + public class ObserverToolMenu extends InventoryMenu { + + public static final String INVENTORY_TITLE = "observer.tools.title"; + public static final int INVENTORY_ROWS = 1; + + private List tools; + + public ObserverToolMenu() { + super(INVENTORY_TITLE, INVENTORY_ROWS); + registerTools(); + } + + public List getTools() { + return tools; + } + + // Register each of the observer tools + // TODO?: Add config options to enable/disable each tool + private void registerTools() { + this.tools = Lists.newArrayList(); + this.tools.add(new FlySpeedTool()); + this.tools.add(new NightVisionTool()); + this.tools.add(new VisibilityTool()); + this.tools.add(new GamemodeTool()); + } + + @Override + public String getTranslatedTitle(MatchPlayer player) { + return ChatColor.AQUA + super.getTranslatedTitle(player); + } + + @Override + public ItemStack[] createWindowContents(MatchPlayer player) { + List items = Lists.newArrayList(); + + items.add(null); + for (InventoryMenuItem tool : tools) { + items.add(tool.createItem(player)); + if (items.size() < ROW_WIDTH - 1) { + items.add(null); + } + } + return items.toArray(new ItemStack[items.size()]); + } + } +} diff --git a/src/main/java/tc/oc/pgm/observers/tools/FlySpeedTool.java b/src/main/java/tc/oc/pgm/observers/tools/FlySpeedTool.java new file mode 100644 index 0000000000..4040c88f8e --- /dev/null +++ b/src/main/java/tc/oc/pgm/observers/tools/FlySpeedTool.java @@ -0,0 +1,93 @@ +package tc.oc.pgm.observers.tools; + +import com.google.common.collect.Lists; +import java.util.List; +import net.md_5.bungee.api.ChatColor; +import org.bukkit.Material; +import tc.oc.component.Component; +import tc.oc.component.render.ComponentRenderers; +import tc.oc.component.types.PersonalizedTranslatable; +import tc.oc.pgm.api.player.MatchPlayer; +import tc.oc.pgm.menu.InventoryMenu; +import tc.oc.pgm.menu.InventoryMenuItem; + +public class FlySpeedTool implements InventoryMenuItem { + + private static String TRANSLATION_KEY = "observer.tools.flyspeed."; + + @Override + public Component getName() { + return new PersonalizedTranslatable("observer.tools.flyspeed"); + } + + @Override + public ChatColor getColor() { + return ChatColor.DARK_RED; + } + + @Override + public List getLore(MatchPlayer player) { + Component flySpeed = FlySpeed.of(player.getBukkit().getFlySpeed()).getName(); + Component lore = + new PersonalizedTranslatable("observer.tools.flyspeed.lore", flySpeed) + .getPersonalizedText() + .color(ChatColor.GRAY); + return Lists.newArrayList(ComponentRenderers.toLegacyText(lore, player.getBukkit())); + } + + @Override + public Material getMaterial(MatchPlayer player) { + return Material.FEATHER; + } + + @Override + public void onInventoryClick(InventoryMenu menu, MatchPlayer player) { + incrementSpeed(player); + menu.refreshWindow(player); + } + + private void incrementSpeed(MatchPlayer player) { + FlySpeed speed = FlySpeed.of(player.getBukkit().getFlySpeed()); + player.getBukkit().setFlySpeed(speed.getNext().getValue()); + } + + public static enum FlySpeed { + NORMAL(ChatColor.YELLOW, 0.1f), + FAST(ChatColor.GOLD, 0.25f), + FASTER(ChatColor.RED, 0.5f), + HYPERSPEED(ChatColor.LIGHT_PURPLE, 0.9f); + + private ChatColor color; + private float value; + + private static FlySpeed[] speeds = values(); + + FlySpeed(ChatColor color, float value) { + this.color = color; + this.value = value; + } + + public float getValue() { + return value; + } + + public Component getName() { + return new PersonalizedTranslatable(TRANSLATION_KEY + this.name().toLowerCase()) + .getPersonalizedText() + .color(color); + } + + public FlySpeed getNext() { + return speeds[(ordinal() + 1) % speeds.length]; + } + + public static FlySpeed of(float value) { + for (FlySpeed speed : FlySpeed.values()) { + if (speed.getValue() == value) { + return speed; + } + } + return NORMAL; + } + } +} diff --git a/src/main/java/tc/oc/pgm/observers/tools/GamemodeTool.java b/src/main/java/tc/oc/pgm/observers/tools/GamemodeTool.java new file mode 100644 index 0000000000..54d4bbd935 --- /dev/null +++ b/src/main/java/tc/oc/pgm/observers/tools/GamemodeTool.java @@ -0,0 +1,96 @@ +package tc.oc.pgm.observers.tools; + +import com.google.common.collect.Lists; +import java.util.List; +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.chat.ClickEvent; +import net.md_5.bungee.api.chat.HoverEvent; +import org.bukkit.Bukkit; +import org.bukkit.GameMode; +import org.bukkit.Material; +import tc.oc.component.Component; +import tc.oc.component.render.ComponentRenderers; +import tc.oc.component.types.PersonalizedText; +import tc.oc.component.types.PersonalizedTranslatable; +import tc.oc.pgm.api.player.MatchPlayer; +import tc.oc.pgm.menu.InventoryMenu; +import tc.oc.pgm.menu.InventoryMenuItem; + +public class GamemodeTool implements InventoryMenuItem { + + @Override + public Component getName() { + return new PersonalizedTranslatable("observer.tools.gamemode"); + } + + @Override + public ChatColor getColor() { + return ChatColor.DARK_AQUA; + } + + @Override + public List getLore(MatchPlayer player) { + Component gamemode = + new PersonalizedTranslatable("gameMode." + player.getGameMode().name().toLowerCase()) + .color(ChatColor.AQUA); + Component lore = + new PersonalizedTranslatable("observer.tools.gamemode.lore", gamemode) + .getPersonalizedText() + .color(ChatColor.GRAY); + return Lists.newArrayList(ComponentRenderers.toLegacyText(lore, player.getBukkit())); + } + + @Override + public Material getMaterial(MatchPlayer player) { + return isCreative(player) ? Material.SEA_LANTERN : Material.PRISMARINE; + } + + @Override + public void onInventoryClick(InventoryMenu menu, MatchPlayer player) { + toggleObserverGameMode(player); + menu.refreshWindow(player); + } + + public void toggleObserverGameMode(MatchPlayer player) { + player.setGameMode(getOppositeMode(player.getGameMode())); + if (player.getGameMode() == GameMode.SPECTATOR) { + player.sendWarning(getToggleMessage(), true); + } else if (isCreative(player)) { + // Note: When WorldEdit is present, this executes a command to ensure the player is not stuck + if (Bukkit.getPluginManager().isPluginEnabled("WorldEdit")) { + player.getBukkit().performCommand("worldedit:!"); + } + } + } + + private boolean isCreative(MatchPlayer player) { + return player.getGameMode().equals(GameMode.CREATIVE); + } + + private Component getToggleMessage() { + Component command = + new PersonalizedText("/tools") + .color(ChatColor.AQUA) + .hoverEvent( + HoverEvent.Action.SHOW_TEXT, + new PersonalizedTranslatable("observer.tools.gamemode.hover") + .getPersonalizedText() + .color(ChatColor.GRAY) + .render()) + .clickEvent(ClickEvent.Action.RUN_COMMAND, "/tools"); + return new PersonalizedTranslatable("observer.tools.gamemode.warning", command) + .getPersonalizedText() + .color(ChatColor.GRAY); + } + + private GameMode getOppositeMode(GameMode mode) { + switch (mode) { + case CREATIVE: + return GameMode.SPECTATOR; + case SPECTATOR: + return GameMode.CREATIVE; + default: + return mode; + } + } +} diff --git a/src/main/java/tc/oc/pgm/observers/tools/NightVisionTool.java b/src/main/java/tc/oc/pgm/observers/tools/NightVisionTool.java new file mode 100644 index 0000000000..eba478f5c4 --- /dev/null +++ b/src/main/java/tc/oc/pgm/observers/tools/NightVisionTool.java @@ -0,0 +1,66 @@ +package tc.oc.pgm.observers.tools; + +import com.google.common.collect.Lists; +import java.util.List; +import net.md_5.bungee.api.ChatColor; +import org.bukkit.Material; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import tc.oc.component.Component; +import tc.oc.component.render.ComponentRenderers; +import tc.oc.component.types.PersonalizedTranslatable; +import tc.oc.pgm.api.player.MatchPlayer; +import tc.oc.pgm.menu.InventoryMenu; +import tc.oc.pgm.menu.InventoryMenuItem; + +public class NightVisionTool implements InventoryMenuItem { + + @Override + public Component getName() { + return new PersonalizedTranslatable("observer.tools.nightvision"); + } + + @Override + public ChatColor getColor() { + return ChatColor.DARK_PURPLE; + } + + @Override + public List getLore(MatchPlayer player) { + Component status = + new PersonalizedTranslatable(hasNightVision(player) ? "misc.on" : "misc.off") + .getPersonalizedText() + .color(hasNightVision(player) ? ChatColor.GREEN : ChatColor.RED); + Component lore = + new PersonalizedTranslatable("observer.tools.nightvision.lore", status) + .getPersonalizedText() + .color(ChatColor.GRAY); + return Lists.newArrayList(ComponentRenderers.toLegacyText(lore, player.getBukkit())); + } + + @Override + public Material getMaterial(MatchPlayer player) { + return hasNightVision(player) ? Material.POTION : Material.GLASS_BOTTLE; + } + + @Override + public void onInventoryClick(InventoryMenu menu, MatchPlayer player) { + toggleNightVision(player); + menu.refreshWindow(player); + } + + private boolean hasNightVision(MatchPlayer player) { + return player.getBukkit().hasPotionEffect(PotionEffectType.NIGHT_VISION); + } + + public void toggleNightVision(MatchPlayer player) { + if (hasNightVision(player)) { + player.getBukkit().removePotionEffect(PotionEffectType.NIGHT_VISION); + } else { + player + .getBukkit() + .addPotionEffect( + new PotionEffect(PotionEffectType.NIGHT_VISION, Integer.MAX_VALUE, 0, true, false)); + } + } +} diff --git a/src/main/java/tc/oc/pgm/observers/tools/VisibilityTool.java b/src/main/java/tc/oc/pgm/observers/tools/VisibilityTool.java new file mode 100644 index 0000000000..9a6718d6a7 --- /dev/null +++ b/src/main/java/tc/oc/pgm/observers/tools/VisibilityTool.java @@ -0,0 +1,64 @@ +package tc.oc.pgm.observers.tools; + +import com.google.common.collect.Lists; +import java.util.List; +import net.md_5.bungee.api.ChatColor; +import org.bukkit.Material; +import tc.oc.component.Component; +import tc.oc.component.render.ComponentRenderers; +import tc.oc.component.types.PersonalizedTranslatable; +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.menu.InventoryMenu; +import tc.oc.pgm.menu.InventoryMenuItem; + +public class VisibilityTool implements InventoryMenuItem { + + @Override + public Component getName() { + return new PersonalizedTranslatable("observer.tools.visibility"); + } + + @Override + public ChatColor getColor() { + return ChatColor.YELLOW; + } + + @Override + public List getLore(MatchPlayer player) { + Component status = + new PersonalizedTranslatable( + isVisible(player) + ? "observer.tools.visibility.shown" + : "observer.tools.visibility.hidden") + .getPersonalizedText() + .color(isVisible(player) ? ChatColor.GREEN : ChatColor.RED); + Component lore = + new PersonalizedTranslatable("observer.tools.visibility.lore", status) + .getPersonalizedText() + .color(ChatColor.GRAY); + return Lists.newArrayList(ComponentRenderers.toLegacyText(lore, player.getBukkit())); + } + + @Override + public Material getMaterial(MatchPlayer player) { + return isVisible(player) ? Material.EYE_OF_ENDER : Material.ENDER_PEARL; + } + + @Override + public void onInventoryClick(InventoryMenu menu, MatchPlayer player) { + toggleObserverVisibility(player); + menu.refreshWindow(player); + } + + public boolean isVisible(MatchPlayer player) { + return player.getSettings().getValue(SettingKey.OBSERVERS) == SettingValue.OBSERVERS_ON; + } + + public void toggleObserverVisibility(MatchPlayer player) { + Settings setting = player.getSettings(); + setting.toggleValue(SettingKey.OBSERVERS); + } +}