diff --git a/build.gradle.kts b/build.gradle.kts index 83a0bc4..16f8cb1 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -14,7 +14,7 @@ version = "0.1.0" description = "The main SR plugin." val mcVersion = "1.20.6-R0.1-SNAPSHOT" -val redisVersion = "5.2.0-beta1" +val redisVersion = "5.2.0-beta2" val invuiVersion = "1.30" val javaVersion = 21 diff --git a/src/main/java/me/unurled/sacredrealms/sr/commands/CommandManager.java b/src/main/java/me/unurled/sacredrealms/sr/commands/CommandManager.java index 80e662f..36bd5df 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/commands/CommandManager.java +++ b/src/main/java/me/unurled/sacredrealms/sr/commands/CommandManager.java @@ -11,6 +11,7 @@ import me.unurled.sacredrealms.sr.commands.admin.ItemCommand; import me.unurled.sacredrealms.sr.commands.admin.LevelCommand; import me.unurled.sacredrealms.sr.commands.admin.SpawnEntityCommand; import me.unurled.sacredrealms.sr.commands.admin.TreasureCommand; +import me.unurled.sacredrealms.sr.commands.player.DifficultyCommand; import me.unurled.sacredrealms.sr.commands.player.ResetAdventureCommand; import me.unurled.sacredrealms.sr.managers.Manager; import org.bukkit.command.PluginCommand; @@ -49,5 +50,6 @@ public class CommandManager extends Manager { registerCommand("treasure", TreasureCommand.class); registerCommand("resetadventure", ResetAdventureCommand.class); + registerCommand("difficulty", DifficultyCommand.class); } } diff --git a/src/main/java/me/unurled/sacredrealms/sr/commands/player/DifficultyCommand.java b/src/main/java/me/unurled/sacredrealms/sr/commands/player/DifficultyCommand.java new file mode 100644 index 0000000..f2cd9e8 --- /dev/null +++ b/src/main/java/me/unurled/sacredrealms/sr/commands/player/DifficultyCommand.java @@ -0,0 +1,214 @@ +package me.unurled.sacredrealms.sr.commands.player; + +import static me.unurled.sacredrealms.sr.utils.Component.NOT_PLAYER; +import static me.unurled.sacredrealms.sr.utils.Component.NO_PERMISSION; +import static me.unurled.sacredrealms.sr.utils.Component.PLAYER_NOT_FOUND; +import static me.unurled.sacredrealms.sr.utils.Component.textComp; + +import java.util.Arrays; +import java.util.List; +import me.unurled.sacredrealms.sr.components.difficulty.Difficulty; +import me.unurled.sacredrealms.sr.components.player.PlayerManager; +import me.unurled.sacredrealms.sr.components.player.SRPlayer; +import me.unurled.sacredrealms.sr.gui.difficulty.DifficultyGUI; +import me.unurled.sacredrealms.sr.managers.Manager; +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.text.Component; +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabExecutor; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import xyz.xenondevs.invui.window.Window; + +/** + * /difficulty ~open a gui for current player~ /difficulty [player] ~open a gui for another player~ + * /difficulty [player] [difficulty] ~set the difficulty for another player~ + */ +public class DifficultyCommand implements TabExecutor { + + public static final String DIFFICULTY_SELF = "sr.difficulty.self"; + public static final String DIFFICULTY_MANAGE = "sr.difficulty.manage"; + public static final String CHOOSE_DIFFICULTY = "Choose Difficulty"; + + public static void usage(CommandSender sender) { + if (sender instanceof Player p) { + if (p.hasPermission(DIFFICULTY_SELF)) { + sender.sendMessage(textComp("Usage: /difficulty")); + } + if (p.hasPermission(DIFFICULTY_MANAGE)) { + sender.sendMessage(textComp("Usage: /difficulty [player]")); + sender.sendMessage(textComp("Usage: /difficulty [player] [difficulty]")); + } + } else { + sender.sendMessage(textComp("Usage: /difficulty [player]")); + sender.sendMessage(textComp("Usage: /difficulty [player] [difficulty]")); + } + } + + private static void playerHandling( + @NotNull CommandSender sender, @NotNull String[] args, @NotNull Player p) { + if (!p.hasPermission(DIFFICULTY_SELF)) { + sender.sendMessage(textComp(NO_PERMISSION)); + return; + } + if (args.length >= 1 && !p.hasPermission(DIFFICULTY_MANAGE)) { + sender.sendMessage(textComp(NO_PERMISSION)); + usage(sender); + return; + } + + if (args.length == 0) { + // open gui for sender + Window window = + Window.single() + .setViewer(p) + .setTitle(CHOOSE_DIFFICULTY) + .setGui(DifficultyGUI.createGui()) + .build(); + window.open(); + return; + } + + if (args.length == 1) { + // open gui for target player + Player target = p.getServer().getPlayer(args[0]); + if (target == null) { + sender.sendMessage(textComp(PLAYER_NOT_FOUND)); + return; + } + Window window = + Window.single() + .setViewer(p) + .setTitle(CHOOSE_DIFFICULTY) + .setGui(DifficultyGUI.createGui()) + .build(); + window.open(); + return; + } + + if (args.length == 2) { + // set difficulty for target player + Player target = p.getServer().getPlayer(args[0]); + if (target == null) { + sender.sendMessage(textComp(PLAYER_NOT_FOUND)); + return; + } + // set difficulty + PlayerManager pm = Manager.getInstance(PlayerManager.class); + if (pm.isSRPlayer(target.getUniqueId())) { + Difficulty difficulty = Difficulty.valueOf(args[1].toUpperCase()); + setDifficulty(sender, pm, target, difficulty); + } + } + } + + private static void setDifficulty( + @NotNull CommandSender sender, + @NotNull PlayerManager pm, + @NotNull Player target, + Difficulty difficulty) { + SRPlayer sr = pm.getPlayer(target.getUniqueId()); + if (sr == null) { + sender.sendMessage(textComp(PLAYER_NOT_FOUND)); + return; + } + sr.setDifficulty(difficulty); + Audience.audience(target) + .sendMessage( + Component.text("Your difficulty has been set to ") + .append(difficulty.getName()) + .append(textComp(".")) + .color(difficulty.getColor())); + } + + private static void consoleHandler( + @NotNull CommandSender sender, @NotNull String @NotNull [] args) { + if (args.length == 0) { + sender.sendMessage(textComp(NOT_PLAYER)); + usage(sender); + return; + } + + Player target = Bukkit.getPlayer(args[0]); + if (target == null) { + sender.sendMessage(textComp(PLAYER_NOT_FOUND)); + return; + } + if (args.length == 1) { + Window window = + Window.single() + .setViewer(target) + .setTitle(CHOOSE_DIFFICULTY) + .setGui(DifficultyGUI.createGui()) + .build(); + window.open(); + } else { + Difficulty difficulty; + try { + difficulty = Difficulty.valueOf(args[1].toUpperCase()); + } catch (IllegalArgumentException e) { + sender.sendMessage(textComp("Invalid difficulty.")); + return; + } + PlayerManager pm = Manager.getInstance(PlayerManager.class); + if (pm.isSRPlayer(target.getUniqueId())) { + setDifficulty(sender, pm, target, difficulty); + } + } + } + + /** + * Executes the given command, returning its success.
+ * If false is returned, then the "usage" plugin.yml entry for this command (if defined) will be + * sent to the player. + * + * @param sender Source of the command + * @param command Command which was executed + * @param label Alias of the command which was used + * @param args Passed command arguments + * @return true if a valid command, otherwise false + */ + @Override + public boolean onCommand( + @NotNull CommandSender sender, + @NotNull Command command, + @NotNull String label, + @NotNull String[] args) { + if (sender instanceof Player p) { + playerHandling(sender, args, p); + } else { + consoleHandler(sender, args); + } + return true; + } + + /** + * Requests a list of possible completions for a command argument. + * + * @param sender Source of the command. For players tab-completing a command inside of a command + * block, this will be the player, not the command block. + * @param command Command which was executed + * @param label Alias of the command which was used + * @param args The arguments passed to the command, including final partial argument to be + * completed + * @return A List of possible completions for the final argument, or null to default to the + * command executor + */ + @Override + public @Nullable List onTabComplete( + @NotNull CommandSender sender, + @NotNull Command command, + @NotNull String label, + @NotNull String @NotNull [] args) { + if (args.length == 0 && sender.hasPermission(DIFFICULTY_MANAGE)) { + return Bukkit.getOnlinePlayers().stream().map(Player::getName).toList(); + } + if (args.length == 1 && sender.hasPermission(DIFFICULTY_MANAGE)) { + return Arrays.stream(Difficulty.values()).map(Enum::name).toList(); + } + return List.of(); + } +} diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/combat/CombatManager.java b/src/main/java/me/unurled/sacredrealms/sr/components/combat/CombatManager.java index 792b964..cf07b9b 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/components/combat/CombatManager.java +++ b/src/main/java/me/unurled/sacredrealms/sr/components/combat/CombatManager.java @@ -46,6 +46,14 @@ public class CombatManager extends Manager { double luck = dLuck - eLuck; + if (!pm.isSRPlayer(player.getUniqueId())) { + return; + } + SRPlayer sr = pm.getPlayer(player.getUniqueId()); + if (sr == null) { + return; + } + if (luck < -1000) { // 100% chance of miss player.sendMessage(comp("You dodged!")); @@ -56,7 +64,7 @@ public class CombatManager extends Manager { // 100% chance of critical hit luck = 10000d; player.playSound(player, Sound.ENTITY_PLAYER_ATTACK_CRIT, 1.0f, 1.0f); - damage = damage * 2; + damage = damage * 2 * sr.getDifficulty().getDamageMultiplier(); } if (luck < 0) { @@ -71,7 +79,7 @@ public class CombatManager extends Manager { // chance of critical hit player.playSound(player, Sound.ENTITY_PLAYER_ATTACK_CRIT, 1.0f, 1.0f); // damage * 2 - damage = damage * 2; + damage = damage * 2 * sr.getDifficulty().getDamageMultiplier(); } // apply damage diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/difficulty/Difficulty.java b/src/main/java/me/unurled/sacredrealms/sr/components/difficulty/Difficulty.java new file mode 100644 index 0000000..4258fd0 --- /dev/null +++ b/src/main/java/me/unurled/sacredrealms/sr/components/difficulty/Difficulty.java @@ -0,0 +1,42 @@ +package me.unurled.sacredrealms.sr.components.difficulty; + +import static me.unurled.sacredrealms.sr.utils.Component.textComp; + +import net.kyori.adventure.text.TextComponent; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextColor; + +public enum Difficulty { + EASY(textComp("Easy"), 1.75, 0.75, NamedTextColor.GREEN), + NORMAL(textComp("Normal"), 1, 1, NamedTextColor.YELLOW), + HARD(textComp("Hard"), 0.75, 1.75, NamedTextColor.RED), + INSANE(textComp("aaInsaneaa"), 0.05, 5, NamedTextColor.DARK_RED); + + private final TextComponent name; + private final double damageMultiplier; + private final double xpMultiplier; + private final TextColor color; + + Difficulty(TextComponent name, double damageMultiplier, double xpMultiplier, TextColor color) { + this.name = name; + this.damageMultiplier = damageMultiplier; + this.xpMultiplier = xpMultiplier; + this.color = color; + } + + public TextComponent getName() { + return this.name; + } + + public double getDamageMultiplier() { + return this.damageMultiplier; + } + + public double getXpMultiplier() { + return this.xpMultiplier; + } + + public TextColor getColor() { + return this.color; + } +} diff --git a/src/main/java/me/unurled/sacredrealms/sr/components/player/SRPlayer.java b/src/main/java/me/unurled/sacredrealms/sr/components/player/SRPlayer.java index cf761f7..b9998e1 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/components/player/SRPlayer.java +++ b/src/main/java/me/unurled/sacredrealms/sr/components/player/SRPlayer.java @@ -11,6 +11,7 @@ import java.util.List; import java.util.Map; import java.util.UUID; import me.unurled.sacredrealms.sr.components.attributes.Attribute; +import me.unurled.sacredrealms.sr.components.difficulty.Difficulty; import me.unurled.sacredrealms.sr.components.item.Item; import me.unurled.sacredrealms.sr.events.player.PlayerKillEvent; import me.unurled.sacredrealms.sr.managers.Manager; @@ -39,6 +40,7 @@ public class SRPlayer { @Expose private List potionEffects = new ArrayList<>(); @Expose private Inventory inventory; @Expose private List treasuresOpened = new ArrayList<>(); + @Expose private Difficulty difficulty; private boolean isClientBuilder = false; private boolean firstTime = true; @@ -255,7 +257,7 @@ public class SRPlayer { public void setExperience(Long experience) { // checks if experience is above next level requirements - if (experience >= levelRequirements.get(level - 1)) { + if (experience >= levelRequirements.get(level - 1) * difficulty.getXpMultiplier()) { PlayerManager pm = Manager.getInstance(PlayerManager.class); setLevel(level + 1); pm.levelUp(this, level - 1); @@ -290,4 +292,12 @@ public class SRPlayer { public void setFirstTime(boolean b) { firstTime = b; } + + public Difficulty getDifficulty() { + return difficulty; + } + + public void setDifficulty(Difficulty difficulty) { + this.difficulty = difficulty; + } } diff --git a/src/main/java/me/unurled/sacredrealms/sr/gui/difficulty/DifficultyGUI.java b/src/main/java/me/unurled/sacredrealms/sr/gui/difficulty/DifficultyGUI.java new file mode 100644 index 0000000..6b6544b --- /dev/null +++ b/src/main/java/me/unurled/sacredrealms/sr/gui/difficulty/DifficultyGUI.java @@ -0,0 +1,28 @@ +package me.unurled.sacredrealms.sr.gui.difficulty; + +import static me.unurled.sacredrealms.sr.utils.Items.cancelItem; +import static me.unurled.sacredrealms.sr.utils.Items.glassPane; + +import me.unurled.sacredrealms.sr.components.difficulty.Difficulty; +import org.jetbrains.annotations.NotNull; +import xyz.xenondevs.invui.gui.Gui; +import xyz.xenondevs.invui.gui.Gui.Builder.Normal; +import xyz.xenondevs.invui.item.Item; +import xyz.xenondevs.invui.item.impl.SimpleItem; + +public class DifficultyGUI { + public static @NotNull Gui createGui() { + Item cancel = new SimpleItem(cancelItem()); + Item glass = new SimpleItem(glassPane()); + Normal gui = + Gui.normal() + .setStructure(".........", "..1.2.3..", "....4....", "....5....") + .addIngredient('5', cancel) + .addIngredient('.', glass) + .addIngredient('1', new DifficultyItem(Difficulty.EASY)) + .addIngredient('2', new DifficultyItem(Difficulty.NORMAL)) + .addIngredient('3', new DifficultyItem(Difficulty.HARD)) + .addIngredient('4', new DifficultyItem(Difficulty.INSANE)); + return gui.build(); + } +} diff --git a/src/main/java/me/unurled/sacredrealms/sr/gui/difficulty/DifficultyItem.java b/src/main/java/me/unurled/sacredrealms/sr/gui/difficulty/DifficultyItem.java new file mode 100644 index 0000000..d7f2ae0 --- /dev/null +++ b/src/main/java/me/unurled/sacredrealms/sr/gui/difficulty/DifficultyItem.java @@ -0,0 +1,96 @@ +package me.unurled.sacredrealms.sr.gui.difficulty; + +import me.unurled.sacredrealms.sr.components.difficulty.Difficulty; +import me.unurled.sacredrealms.sr.components.player.PlayerManager; +import me.unurled.sacredrealms.sr.components.player.SRPlayer; +import me.unurled.sacredrealms.sr.managers.Manager; +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.ItemStack; +import org.jetbrains.annotations.NotNull; +import xyz.xenondevs.invui.item.Item; +import xyz.xenondevs.invui.item.ItemProvider; +import xyz.xenondevs.invui.item.builder.ItemBuilder; +import xyz.xenondevs.invui.item.impl.AbstractItem; +import xyz.xenondevs.invui.window.Window; + +public class DifficultyItem extends AbstractItem { + + private Difficulty difficulty; + + public DifficultyItem(Difficulty difficulty) { + this.difficulty = difficulty; + } + + /** + * Gets the {@link ItemProvider}. This method gets called every time a {@link Window} is notified + * ({@link #notifyWindows()}). + * + * @return The {@link ItemProvider} + */ + @Override + public ItemProvider getItemProvider() { + return switch (difficulty) { + case EASY -> + new ItemBuilder(Material.BREAD) + .setDisplayName("Easy") + .addLoreLines( + "Is you are a new player or", + "someone who just want to", + "enjoy the game without " + "grinding too much", + "", + "Click Here!"); + case NORMAL -> + new ItemBuilder(Material.GRASS_BLOCK) + .setDisplayName("Normal") + .addLoreLines( + "Is you are a regular player", + "who want to enjoy the game", + "without too much grinding", + "", + "Click Here!"); + case HARD -> + new ItemBuilder(Material.DIAMOND_SWORD) + .setDisplayName("Hard") + .addLoreLines( + "Is you are a hardcore player", + "who want to enjoy the game", + "with some grinding", + "", + "Click Here!"); + case INSANE -> + new ItemBuilder(Material.NETHERITE_SWORD) + .setDisplayName("Insane") + .addLoreLines( + "Is you are a pro player", + "who want to enjoy the game", + "with a lot of grinding", + "some things are hidden very well here...", + "", + "Click Here!"); + }; + } + + /** + * A method called if the {@link ItemStack} associated to this {@link Item} has been clicked by a + * player. + * + * @param clickType The {@link ClickType} the {@link Player} performed. + * @param player The {@link Player} who clicked on the {@link ItemStack}. + * @param event The {@link InventoryClickEvent} associated with this click. + */ + @Override + public void handleClick( + @NotNull ClickType clickType, @NotNull Player player, @NotNull InventoryClickEvent event) { + // Set the player's difficulty + PlayerManager pm = Manager.getInstance(PlayerManager.class); + if (pm.isSRPlayer(player.getUniqueId())) { + SRPlayer sr = pm.getPlayer(player.getUniqueId()); + if (sr != null) { + sr.setDifficulty(difficulty); + } + } + } +} diff --git a/src/main/java/me/unurled/sacredrealms/sr/gui/treasure/TreasureGUI.java b/src/main/java/me/unurled/sacredrealms/sr/gui/treasure/TreasureGUI.java index e65a141..2170819 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/gui/treasure/TreasureGUI.java +++ b/src/main/java/me/unurled/sacredrealms/sr/gui/treasure/TreasureGUI.java @@ -1,6 +1,8 @@ package me.unurled.sacredrealms.sr.gui.treasure; import static me.unurled.sacredrealms.sr.utils.Component.textComp; +import static me.unurled.sacredrealms.sr.utils.Items.cancelItem; +import static me.unurled.sacredrealms.sr.utils.Items.glassPane; import me.unurled.sacredrealms.sr.components.treasure.Treasure; import org.bukkit.Material; @@ -18,10 +20,7 @@ public class TreasureGUI { @NotNull public static Gui createGui(@NotNull Treasure treasure) { - ItemStack itemStack = new ItemStack(Material.GRAY_STAINED_GLASS_PANE); - ItemMeta itemMeta = itemStack.getItemMeta(); - itemMeta.displayName(textComp("")); - itemStack.setItemMeta(itemMeta); + ItemStack itemStack = glassPane(); Item pane = new SimpleItem(itemStack); Normal gui = Gui.normal() @@ -38,10 +37,7 @@ public class TreasureGUI { } } - ItemStack cancel = new ItemStack(Material.BARRIER); - ItemMeta cancelMeta = cancel.getItemMeta(); - cancelMeta.displayName(textComp("Cancel")); - cancel.setItemMeta(cancelMeta); + ItemStack cancel = cancelItem(); ItemStack confirm = new ItemStack(Material.GREEN_STAINED_GLASS_PANE); ItemMeta confirmMeta = confirm.getItemMeta(); diff --git a/src/main/java/me/unurled/sacredrealms/sr/utils/Component.java b/src/main/java/me/unurled/sacredrealms/sr/utils/Component.java index 55d031e..9754212 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/utils/Component.java +++ b/src/main/java/me/unurled/sacredrealms/sr/utils/Component.java @@ -10,6 +10,8 @@ public class Component { public static final String NO_PERMISSION = "You do not have permission to use this command."; public static final String ERROR = "An error occurred while executing this command. Please" + " contact an admin."; + public static final String PLAYER_NOT_FOUND = "Player not found."; + private static MiniMessage miniMessage; private Component() {} diff --git a/src/main/java/me/unurled/sacredrealms/sr/utils/Items.java b/src/main/java/me/unurled/sacredrealms/sr/utils/Items.java index 8f53f56..69a93ee 100644 --- a/src/main/java/me/unurled/sacredrealms/sr/utils/Items.java +++ b/src/main/java/me/unurled/sacredrealms/sr/utils/Items.java @@ -1,6 +1,7 @@ package me.unurled.sacredrealms.sr.utils; import static me.unurled.sacredrealms.sr.utils.Component.comp; +import static me.unurled.sacredrealms.sr.utils.Component.textComp; import static me.unurled.sacredrealms.sr.utils.SRPlayerUtils.syncSRToPlayer; import java.util.ArrayList; @@ -17,6 +18,7 @@ import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.inventory.EntityEquipment; import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.persistence.PersistentDataContainer; import org.bukkit.persistence.PersistentDataType; import org.jetbrains.annotations.NotNull; @@ -26,6 +28,22 @@ public class Items { private Items() {} + public static @NotNull ItemStack cancelItem() { + ItemStack cancel = new ItemStack(Material.BARRIER); + ItemMeta cancelMeta = cancel.getItemMeta(); + cancelMeta.displayName(textComp("Cancel")); + cancel.setItemMeta(cancelMeta); + return cancel; + } + + public static @NotNull ItemStack glassPane() { + ItemStack itemStack = new ItemStack(Material.GRAY_STAINED_GLASS_PANE); + ItemMeta itemMeta = itemStack.getItemMeta(); + itemMeta.displayName(textComp("")); + itemStack.setItemMeta(itemMeta); + return itemStack; + } + public static @NotNull Double getTotalAttribute(@NotNull Player player, Attribute attribute) { return getTotalAttribute(player.getEquipment(), attribute); } diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 3bf0385..c7c51b7 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -38,6 +38,12 @@ permissions: sr.treasure.manage: default: op description: When the player has permission for the command /treasure create + sr.difficulty.manage: + default: op + description: When the player has permission for the command /difficulty set player + sr.difficulty.self: + default: not op + description: When the player has permission for the command /difficulty commands: attributes: @@ -56,3 +62,5 @@ commands: description: Reset the adventure of the player treasure: description: Create a treasure chest. + difficulty: + description: Set the difficulty of the player.