From 817aa2cb8d20fd1e9bb4608f4c525607f88c39b2 Mon Sep 17 00:00:00 2001 From: GB6 Date: Wed, 3 Apr 2019 15:58:13 +0200 Subject: [PATCH] - Added the alchemist - Moved the ee enchanter ee tinkerer ee alchemist into separate commands. (/enchanter /tinkerer /alchemist) - Added {user_holding} {opponent_holding} variables. - Added {user_is_swimming} {opponent_is_swimming} variables. - CONSOLE_COMMAND, MESSAGE and PLAYER_COMMAND now support multiples lines of string to handle --- .../java/com/songoda/epicenchants/Action.java | 26 +- .../epicenchants/commands/CustomCommand.java | 37 +++ .../epicenchants/commands/EnchantCommand.java | 20 +- .../epicenchants/effect/EffectExecutor.java | 19 +- .../effect/effects/ConsoleCommand.java | 6 +- .../epicenchants/effect/effects/Explode.java | 20 ++ .../epicenchants/effect/effects/Message.java | 10 +- .../effect/effects/ModifyBlock.java | 6 + .../effect/effects/PlayerCommand.java | 9 +- .../epicenchants/effect/effects/StealExp.java | 14 +- .../songoda/epicenchants/enums/GiveType.java | 2 +- .../epicenchants/enums/TriggerType.java | 3 +- .../listeners/PlayerListener.java | 2 + .../listeners/item/BookListener.java | 2 +- .../epicenchants/managers/CommandManager.java | 2 + .../epicenchants/managers/FileManager.java | 5 +- .../epicenchants/menus/AlchemistMenu.java | 274 +++++++++++++++++- .../epicenchants/menus/EnchanterMenu.java | 8 +- .../epicenchants/menus/MainInfoMenu.java | 4 +- .../epicenchants/menus/TinkererMenu.java | 4 +- .../epicenchants/objects/Condition.java | 17 +- .../songoda/epicenchants/objects/Group.java | 1 + .../epicenchants/objects/LeveledModifier.java | 15 +- .../epicenchants/utils/EnchantUtils.java | 4 +- .../epicenchants/utils/SpecialItems.java | 2 +- .../epicenchants/utils/objects/FastInv.java | 52 +--- .../utils/single/ConfigParser.java | 11 +- .../utils/single/GeneralUtils.java | 48 ++- .../utils/single/Placeholders.java | 60 +++- .../utils/single/RomanNumber.java | 1 + core/src/main/resources/actions.yml | 19 +- core/src/main/resources/groups.yml | 5 + .../legacy/items/special-items.yml | 2 +- .../legacy/menus/alchemist-menu.yml | 53 ++++ .../master/menus/alchemist-menu.yml | 50 ++++ 35 files changed, 637 insertions(+), 176 deletions(-) create mode 100644 core/src/main/java/com/songoda/epicenchants/commands/CustomCommand.java create mode 100644 core/src/main/java/com/songoda/epicenchants/effect/effects/Explode.java create mode 100644 core/src/main/resources/version-dependent/legacy/menus/alchemist-menu.yml create mode 100644 core/src/main/resources/version-dependent/master/menus/alchemist-menu.yml diff --git a/core/src/main/java/com/songoda/epicenchants/Action.java b/core/src/main/java/com/songoda/epicenchants/Action.java index ecb4d46..7bc4f65 100644 --- a/core/src/main/java/com/songoda/epicenchants/Action.java +++ b/core/src/main/java/com/songoda/epicenchants/Action.java @@ -8,15 +8,18 @@ import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.entity.Player; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + import static com.songoda.epicenchants.utils.single.GeneralUtils.color; public class Action { private FileConfiguration config; - private String prefix; public void perform(CommandSender sender, String node, Placeholder... placeholders) { if (config.isString(node)) { - sender.sendMessage(getMessage(node, placeholders)); + getMessage(node, placeholders).forEach(sender::sendMessage); return; } @@ -26,8 +29,8 @@ public class Action { ConfigurationSection section = config.getConfigurationSection(node); - if (section.isString("message")) { - sender.sendMessage(getMessage(node + ".message", placeholders)); + if (section.isString("message") || section.isList("message")) { + getMessage(node + ".message", placeholders).forEach(sender::sendMessage); } if (!(sender instanceof Player)) { @@ -46,18 +49,19 @@ public class Action { } - public String getMessage(String node, Placeholder... placeholders) { - String string = config.getString(node); + public List getMessage(String node, Placeholder... placeholders) { + List output = config.isList(node) ? config.getStringList(node) : Collections.singletonList(config.getString(node)); - for (Placeholder placeholder : placeholders) { - string = string.replace(placeholder.getPlaceholder(), placeholder.getToReplace().toString()); - } + return output.stream().map(s -> { + for (Placeholder placeholder : placeholders) { + s = s.replace(placeholder.getPlaceholder(), placeholder.getToReplace().toString()); + } - return color(string); + return color(s); + }).collect(Collectors.toList()); } public void load(FileConfiguration config) { this.config = config; - this.prefix = config.getString("general.prefix"); } } diff --git a/core/src/main/java/com/songoda/epicenchants/commands/CustomCommand.java b/core/src/main/java/com/songoda/epicenchants/commands/CustomCommand.java new file mode 100644 index 0000000..7047888 --- /dev/null +++ b/core/src/main/java/com/songoda/epicenchants/commands/CustomCommand.java @@ -0,0 +1,37 @@ +package com.songoda.epicenchants.commands; + +import co.aikar.commands.BaseCommand; +import co.aikar.commands.annotation.*; +import com.songoda.epicenchants.EpicEnchants; +import com.songoda.epicenchants.menus.AlchemistMenu; +import com.songoda.epicenchants.menus.EnchanterMenu; +import com.songoda.epicenchants.menus.TinkererMenu; +import org.bukkit.entity.Player; + +public class CustomCommand extends BaseCommand { + + @Dependency("instance") + private EpicEnchants instance; + + @CommandAlias("%enchanter") + @Description("Opens the Enchanter") + @CommandPermission("epicenchants.enchanter") + public void onEnchanter(Player player) { + new EnchanterMenu(instance, instance.getFileManager().getConfiguration("menus/enchanter-menu"), player).open(player); + } + + @CommandAlias("%tinkerer") + @Description("Opens the Tinkerer") + @CommandPermission("epicenchants.tinkerer") + public void onTinkerer(Player player) { + new TinkererMenu(instance, instance.getFileManager().getConfiguration("menus/tinkerer-menu")).open(player); + } + + @CommandAlias("%alchemist") + @Description("Opens the Alchemist") + @CommandPermission("epicenchants.alchemist") + public void onAlchemist(Player player) { + new AlchemistMenu(instance, instance.getFileManager().getConfiguration("menus/alchemist-menu")).open(player); + } + +} diff --git a/core/src/main/java/com/songoda/epicenchants/commands/EnchantCommand.java b/core/src/main/java/com/songoda/epicenchants/commands/EnchantCommand.java index e43bcc0..83e76d2 100644 --- a/core/src/main/java/com/songoda/epicenchants/commands/EnchantCommand.java +++ b/core/src/main/java/com/songoda/epicenchants/commands/EnchantCommand.java @@ -5,8 +5,6 @@ import co.aikar.commands.CommandHelp; import co.aikar.commands.annotation.*; import com.songoda.epicenchants.EpicEnchants; import com.songoda.epicenchants.enums.EnchantResult; -import com.songoda.epicenchants.menus.EnchanterMenu; -import com.songoda.epicenchants.menus.TinkererMenu; import com.songoda.epicenchants.objects.Enchant; import com.songoda.epicenchants.objects.Group; import org.apache.commons.lang3.tuple.Pair; @@ -24,20 +22,6 @@ public class EnchantCommand extends BaseCommand { @Dependency("instance") private EpicEnchants instance; - @Subcommand("%enchanter") - @Description("Opens the Enchanter") - @CommandPermission("epicenchants.enchanter") - public void onEnchanter(Player player) { - new EnchanterMenu(instance, instance.getFileManager().getConfiguration("menus/enchanter-menu"), player).open(player); - } - - @Subcommand("%tinkerer") - @Description("Opens the Tinkerer") - @CommandPermission("epicenchants.tinkerer") - public void onTinkerer(Player player) { - new TinkererMenu(instance, instance.getFileManager().getConfiguration("menus/tinkerer-menu")).open(player); - } - //ee give book [player] [enchant] @Subcommand("give book") @CommandCompletion("@players @enchants @levels @increment @increment") @@ -68,10 +52,10 @@ public class EnchantCommand extends BaseCommand { //ee give item [giveType] [player] @Subcommand("give item") - @CommandCompletion("@players @giveType @nothing @nothing") + @CommandCompletion("@giveType @players @nothing @nothing") @Description("Give enchant books to players") @CommandPermission("epicenchants.give.item") - public void onGiveItem(CommandSender sender, @Flags("other") Player target, String giveType, @Optional Integer amount, @Optional Integer successRate) { + public void onGiveItem(CommandSender sender, String giveType, @Flags("other") Player target, @Optional Integer amount, @Optional Integer successRate) { String messageKey; switch (giveType.toLowerCase()) { case "whitescroll": diff --git a/core/src/main/java/com/songoda/epicenchants/effect/EffectExecutor.java b/core/src/main/java/com/songoda/epicenchants/effect/EffectExecutor.java index 33cd5ac..a041617 100644 --- a/core/src/main/java/com/songoda/epicenchants/effect/EffectExecutor.java +++ b/core/src/main/java/com/songoda/epicenchants/effect/EffectExecutor.java @@ -13,21 +13,31 @@ import org.bukkit.event.Event; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Collections; +import java.util.Optional; import java.util.Set; import java.util.function.Consumer; +import java.util.stream.Collectors; -import static com.songoda.epicenchants.effect.EffectExecutor.Who.OPPONENT; -import static com.songoda.epicenchants.effect.EffectExecutor.Who.USER; +import static com.songoda.epicenchants.effect.EffectExecutor.Who.*; public abstract class EffectExecutor { @Getter private final ConfigurationSection section; @Getter private final Set triggerTypes; + private final Set simultaneous; private final Condition condition; public EffectExecutor(ConfigurationSection section) { this.section = section; this.triggerTypes = GeneralUtils.parseTrigger(section.getString("trigger")); this.condition = Condition.of(section.getString("condition")); + this.simultaneous = section.isConfigurationSection("effects") ? section.getConfigurationSection("effects").getKeys(false).stream() + .map(s -> "effects." + s) + .map(section::getConfigurationSection) + .map(EffectManager::getEffect) + .filter(Optional::isPresent) + .map(Optional::get) + .collect(Collectors.toSet()) : Collections.emptySet(); } public void testAndRun(@NotNull Player user, @Nullable LivingEntity opponent, int level, TriggerType type, Event event, EventType eventType) { @@ -45,10 +55,11 @@ public abstract class EffectExecutor { if (this instanceof EffectEventExecutor) { ((EffectEventExecutor) this).execute(user, opponent, level, event, eventType); - return; + } else { + execute(user, opponent, level, eventType); } - execute(user, opponent, level, eventType); + simultaneous.forEach(e -> e.execute(user, opponent, level, eventType)); } public abstract void execute(@NotNull Player user, @Nullable LivingEntity opponent, int level, EventType eventType); diff --git a/core/src/main/java/com/songoda/epicenchants/effect/effects/ConsoleCommand.java b/core/src/main/java/com/songoda/epicenchants/effect/effects/ConsoleCommand.java index 576c3d0..cd2e6a1 100644 --- a/core/src/main/java/com/songoda/epicenchants/effect/effects/ConsoleCommand.java +++ b/core/src/main/java/com/songoda/epicenchants/effect/effects/ConsoleCommand.java @@ -2,6 +2,7 @@ package com.songoda.epicenchants.effect.effects; import com.songoda.epicenchants.effect.EffectExecutor; import com.songoda.epicenchants.enums.EventType; +import com.songoda.epicenchants.utils.single.GeneralUtils; import com.songoda.epicenchants.utils.single.Placeholders; import org.bukkit.Bukkit; import org.bukkit.configuration.ConfigurationSection; @@ -16,7 +17,8 @@ public class ConsoleCommand extends EffectExecutor { @Override public void execute(@NotNull Player user, LivingEntity opponent, int level, EventType eventType) { - Bukkit.dispatchCommand(Bukkit.getConsoleSender(), - Placeholders.setPlaceholders(getSection().getString("command"), user, opponent, level)); + GeneralUtils.getString(getSection(), "command").stream() + .map(s -> Placeholders.setPlaceholders(s, user, opponent, level)) + .forEach(s -> Bukkit.dispatchCommand(Bukkit.getConsoleSender(), s)); } } diff --git a/core/src/main/java/com/songoda/epicenchants/effect/effects/Explode.java b/core/src/main/java/com/songoda/epicenchants/effect/effects/Explode.java new file mode 100644 index 0000000..b19b2a9 --- /dev/null +++ b/core/src/main/java/com/songoda/epicenchants/effect/effects/Explode.java @@ -0,0 +1,20 @@ +package com.songoda.epicenchants.effect.effects; + +import com.songoda.epicenchants.effect.EffectExecutor; +import com.songoda.epicenchants.enums.EventType; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class Explode extends EffectExecutor { + public Explode(ConfigurationSection section) { + super(section); + } + + @Override + public void execute(@NotNull Player user, @Nullable LivingEntity opponent, int level, EventType eventType) { + consume(entity -> entity.getWorld().createExplosion(entity.getLocation(), getSection().getInt("magnitude")), user, opponent); + } +} diff --git a/core/src/main/java/com/songoda/epicenchants/effect/effects/Message.java b/core/src/main/java/com/songoda/epicenchants/effect/effects/Message.java index 242013f..42122bf 100644 --- a/core/src/main/java/com/songoda/epicenchants/effect/effects/Message.java +++ b/core/src/main/java/com/songoda/epicenchants/effect/effects/Message.java @@ -2,14 +2,13 @@ package com.songoda.epicenchants.effect.effects; import com.songoda.epicenchants.effect.EffectExecutor; import com.songoda.epicenchants.enums.EventType; +import com.songoda.epicenchants.utils.single.GeneralUtils; import com.songoda.epicenchants.utils.single.Placeholders; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; -import static com.songoda.epicenchants.utils.single.GeneralUtils.color; - public class Message extends EffectExecutor { public Message(ConfigurationSection section) { super(section); @@ -17,7 +16,10 @@ public class Message extends EffectExecutor { @Override public void execute(@NotNull Player user, LivingEntity opponent, int level, EventType eventType) { - if (eventType == EventType.ON || eventType == EventType.NONE) - consume(entity -> entity.sendMessage(color(Placeholders.setPlaceholders(getSection().getString("message"), user, opponent, level))), user, opponent); + if (eventType == EventType.ON || eventType == EventType.NONE) { + consume(entity -> GeneralUtils.getString(getSection(), "message").stream() + .map(s -> Placeholders.setPlaceholders(s, user, opponent, level)) + .forEach(entity::sendMessage), user, opponent); + } } } diff --git a/core/src/main/java/com/songoda/epicenchants/effect/effects/ModifyBlock.java b/core/src/main/java/com/songoda/epicenchants/effect/effects/ModifyBlock.java index c6aaf42..7e0f31c 100644 --- a/core/src/main/java/com/songoda/epicenchants/effect/effects/ModifyBlock.java +++ b/core/src/main/java/com/songoda/epicenchants/effect/effects/ModifyBlock.java @@ -23,6 +23,12 @@ public class ModifyBlock extends EffectEventExecutor { } Block block = event instanceof BlockEvent ? ((BlockEvent) event).getBlock() : ((PlayerInteractEvent) event).getClickedBlock(); + + if (getSection().getBoolean("break-naturally")) { + block.breakNaturally(); + return; + } + block.setType(Material.getMaterial(getSection().getString("material"))); } } diff --git a/core/src/main/java/com/songoda/epicenchants/effect/effects/PlayerCommand.java b/core/src/main/java/com/songoda/epicenchants/effect/effects/PlayerCommand.java index ed59e12..7234fdf 100644 --- a/core/src/main/java/com/songoda/epicenchants/effect/effects/PlayerCommand.java +++ b/core/src/main/java/com/songoda/epicenchants/effect/effects/PlayerCommand.java @@ -2,6 +2,7 @@ package com.songoda.epicenchants.effect.effects; import com.songoda.epicenchants.effect.EffectExecutor; import com.songoda.epicenchants.enums.EventType; +import com.songoda.epicenchants.utils.single.GeneralUtils; import com.songoda.epicenchants.utils.single.Placeholders; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.entity.LivingEntity; @@ -9,8 +10,7 @@ import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; import static com.songoda.epicenchants.effect.EffectExecutor.Who.OPPONENT; -import static com.songoda.epicenchants.enums.EventType.NONE; -import static com.songoda.epicenchants.enums.EventType.ON; +import static com.songoda.epicenchants.enums.EventType.*; public class PlayerCommand extends EffectExecutor { public PlayerCommand(ConfigurationSection section) { @@ -27,7 +27,8 @@ public class PlayerCommand extends EffectExecutor { return; } - consume(entity -> ((Player) entity).performCommand(Placeholders.setPlaceholders(getSection().getString("command"), user, opponent, level)), user, opponent); - + consume(entity -> GeneralUtils.getString(getSection(), "message").stream() + .map(s -> Placeholders.setPlaceholders(s, user, opponent, level)) + .forEach(((Player) entity)::performCommand), user, opponent); } } diff --git a/core/src/main/java/com/songoda/epicenchants/effect/effects/StealExp.java b/core/src/main/java/com/songoda/epicenchants/effect/effects/StealExp.java index 05da780..0d9fb4f 100644 --- a/core/src/main/java/com/songoda/epicenchants/effect/effects/StealExp.java +++ b/core/src/main/java/com/songoda/epicenchants/effect/effects/StealExp.java @@ -8,8 +8,7 @@ import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import static com.songoda.epicenchants.utils.single.Experience.changeExp; -import static com.songoda.epicenchants.utils.single.Experience.getExp; +import static com.songoda.epicenchants.utils.single.Experience.*; public class StealExp extends EffectExecutor { public StealExp(ConfigurationSection section) { @@ -24,13 +23,16 @@ public class StealExp extends EffectExecutor { return; } - Player player = (Player) opponent; + Player opponentPlayer = (Player) opponent; + if (amount > getExp(opponentPlayer)) { + amount = getExp(opponentPlayer); + } - if (getExp(player) - amount <= 0) { - changeExp(player, 0); + if (getExp(opponentPlayer) - amount <= 0) { + changeExp(opponentPlayer, 0); } else { - changeExp(player, (int) -amount); + changeExp(opponentPlayer, (int) -amount); } if (getExp(user) + amount <= 0) { diff --git a/core/src/main/java/com/songoda/epicenchants/enums/GiveType.java b/core/src/main/java/com/songoda/epicenchants/enums/GiveType.java index e51c094..b3631bc 100644 --- a/core/src/main/java/com/songoda/epicenchants/enums/GiveType.java +++ b/core/src/main/java/com/songoda/epicenchants/enums/GiveType.java @@ -1,5 +1,5 @@ package com.songoda.epicenchants.enums; public enum GiveType { - WHITE_SCROLL, BLACK_SCROLL, DUST + WHITE_SCROLL, BLACK_SCROLL } diff --git a/core/src/main/java/com/songoda/epicenchants/enums/TriggerType.java b/core/src/main/java/com/songoda/epicenchants/enums/TriggerType.java index a509a77..fe58ab6 100644 --- a/core/src/main/java/com/songoda/epicenchants/enums/TriggerType.java +++ b/core/src/main/java/com/songoda/epicenchants/enums/TriggerType.java @@ -27,5 +27,6 @@ public enum TriggerType { BLOCK_BREAK, REPEATING, - RIGHT_CLICK + RIGHT_CLICK, + LEFT_CLICK } diff --git a/core/src/main/java/com/songoda/epicenchants/listeners/PlayerListener.java b/core/src/main/java/com/songoda/epicenchants/listeners/PlayerListener.java index 452f43b..aaa0335 100644 --- a/core/src/main/java/com/songoda/epicenchants/listeners/PlayerListener.java +++ b/core/src/main/java/com/songoda/epicenchants/listeners/PlayerListener.java @@ -58,6 +58,8 @@ public class PlayerListener implements Listener { public void onPlayerInteract(PlayerInteractEvent event) { if (event.getAction() == Action.RIGHT_CLICK_AIR || event.getAction() == Action.RIGHT_CLICK_BLOCK) { instance.getEnchantUtils().handlePlayer(event.getPlayer(), null, event, RIGHT_CLICK); + } else if (event.getAction() == Action.LEFT_CLICK_AIR || event.getAction() == Action.LEFT_CLICK_BLOCK) { + instance.getEnchantUtils().handlePlayer(event.getPlayer(), null, event, LEFT_CLICK); } } diff --git a/core/src/main/java/com/songoda/epicenchants/listeners/item/BookListener.java b/core/src/main/java/com/songoda/epicenchants/listeners/item/BookListener.java index d96c0ed..edc874a 100644 --- a/core/src/main/java/com/songoda/epicenchants/listeners/item/BookListener.java +++ b/core/src/main/java/com/songoda/epicenchants/listeners/item/BookListener.java @@ -77,7 +77,7 @@ public class BookListener extends ItemListener { return; } - Group group = instance.getGroupManager().getValueUnsafe(clicked.getString("group")); + Group group = instance.getGroupManager().getValue(clicked.getString("group")).orElseThrow(() -> new IllegalStateException("Book without group!")); Optional enchant = instance.getEnchantManager().getRandomEnchant(group); diff --git a/core/src/main/java/com/songoda/epicenchants/managers/CommandManager.java b/core/src/main/java/com/songoda/epicenchants/managers/CommandManager.java index 980196d..d6da6c2 100644 --- a/core/src/main/java/com/songoda/epicenchants/managers/CommandManager.java +++ b/core/src/main/java/com/songoda/epicenchants/managers/CommandManager.java @@ -3,6 +3,7 @@ package com.songoda.epicenchants.managers; import co.aikar.commands.BukkitCommandManager; import co.aikar.commands.InvalidCommandArgument; import com.songoda.epicenchants.EpicEnchants; +import com.songoda.epicenchants.commands.CustomCommand; import com.songoda.epicenchants.commands.EnchantCommand; import com.songoda.epicenchants.enums.GiveType; import com.songoda.epicenchants.objects.Enchant; @@ -70,5 +71,6 @@ public class CommandManager extends BukkitCommandManager { // COMMANDS registerCommand(new EnchantCommand()); + registerCommand(new CustomCommand()); } } diff --git a/core/src/main/java/com/songoda/epicenchants/managers/FileManager.java b/core/src/main/java/com/songoda/epicenchants/managers/FileManager.java index 95df8a5..008dc41 100644 --- a/core/src/main/java/com/songoda/epicenchants/managers/FileManager.java +++ b/core/src/main/java/com/songoda/epicenchants/managers/FileManager.java @@ -24,6 +24,7 @@ public class FileManager extends Manager { of("menus/main-info-menu.yml", true), of("menus/enchanter-menu.yml", true, true), of("menus/tinkerer-menu.yml", true, true), + of("menus/alchemist-menu.yml", true, true), of("menus/groups/simple-menu.yml", false), of("menus/groups/unique-menu.yml", false), of("menus/groups/elite-menu.yml", false), @@ -38,10 +39,8 @@ public class FileManager extends Manager { public FileManager(EpicEnchants instance) { super(instance); - directory = instance.getVersion() > 12 ? "master" : "legacy"; - - Bukkit.getConsoleSender().sendMessage("Using the " + directory + " because version is 1." + instance.getVersion()); + Bukkit.getConsoleSender().sendMessage("Using the " + directory + " configurations because version is 1." + instance.getVersion()); } public void loadFiles() { diff --git a/core/src/main/java/com/songoda/epicenchants/menus/AlchemistMenu.java b/core/src/main/java/com/songoda/epicenchants/menus/AlchemistMenu.java index 297eecc..b9fdc82 100644 --- a/core/src/main/java/com/songoda/epicenchants/menus/AlchemistMenu.java +++ b/core/src/main/java/com/songoda/epicenchants/menus/AlchemistMenu.java @@ -1,34 +1,288 @@ package com.songoda.epicenchants.menus; import com.songoda.epicenchants.EpicEnchants; +import com.songoda.epicenchants.objects.Enchant; +import com.songoda.epicenchants.objects.Group; +import com.songoda.epicenchants.objects.Placeholder; import com.songoda.epicenchants.utils.objects.FastInv; import com.songoda.epicenchants.utils.objects.ItemBuilder; +import com.songoda.epicenchants.utils.single.GeneralUtils; +import de.tr7zw.itemnbtapi.NBTItem; import org.bukkit.Material; import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.ItemStack; +import java.util.HashSet; +import java.util.Set; + +import static com.songoda.epicenchants.objects.Placeholder.of; +import static com.songoda.epicenchants.utils.single.Experience.*; import static com.songoda.epicenchants.utils.single.GeneralUtils.*; public class AlchemistMenu extends FastInv { + private final EpicEnchants instance; + private final FileConfiguration config; + private final int LEFT_SLOT, RIGHT_SLOT, PREVIEW_SLOT, ACCEPT_SLOT; + private final ItemStack PREVIEW_ITEM, ACCEPT_ITEM; + public AlchemistMenu(EpicEnchants instance, FileConfiguration config) { super(config.getInt("rows") * 9, color(config.getString("title"))); + this.instance = instance; + this.config = config; + + LEFT_SLOT = config.getInt("left-slot"); + RIGHT_SLOT = config.getInt("right-slot"); + PREVIEW_SLOT = config.getInt("preview-slot"); + ACCEPT_SLOT = config.getInt("accept-slot"); + + PREVIEW_ITEM = new ItemBuilder(config.getConfigurationSection("contents.preview")).build(); + ACCEPT_ITEM = new ItemBuilder(config.getConfigurationSection("contents.accept-before")).build(); + if (config.isConfigurationSection("fill")) { fill(new ItemBuilder(config.getConfigurationSection("fill")).build()); } + Set filter = new HashSet() {{ + add("preview"); + add("accept-before"); + add("accept-after"); + }}; + config.getConfigurationSection("contents").getKeys(false) .stream() + .filter(s -> !filter.contains(s)) .map(s -> "contents." + s) .map(config::getConfigurationSection) - .forEach(section -> { - addItem(getSlots(config.getString("slot")), new ItemBuilder(section).build(), event -> { - if (section.getName().equalsIgnoreCase("left-item") || section.getName().equalsIgnoreCase("right-item")) { - if (getInventory().getItem(event.getSlot()) != null && getInventory().getItem(event.getSlot()).getType() != Material.AIR) { - event.getPlayer().getInventory().addItem(getInventory().getItem(event.getSlot())); - getInventory().clear(event.getSlot()); - } - } - }); - }); + .forEach(section -> addItem(getSlots(config.getString("slot")), new ItemBuilder(section).build())); + + clear(RIGHT_SLOT); + clear(LEFT_SLOT); + + updateSlots(); + + // Player clicked an item in tinkerer + onClick(event -> { + if (event.getEvent().getClickedInventory() == null && event.getInventory().equals(this)) { + return; + } + + int slot = event.getSlot(); + + if (slot != RIGHT_SLOT && slot != LEFT_SLOT) { + return; + } + + if (getInventory().getItem(slot) != null && getInventory().getItem(slot).getType() != Material.AIR) { + event.getPlayer().getInventory().addItem(getInventory().getItem(slot)); + getInventory().clear(slot); + updateSlots(); + } + }); + + // Player clicked his own inv + onClick(event -> { + if (event.getEvent().getClickedInventory() == null || event.getEvent().getClickedInventory().getType() != InventoryType.PLAYER) { + return; + } + + ItemStack itemStack = event.getItem(); + + if (!handleItem(event.getPlayer(), itemStack)) { + return; + } + + if (itemStack.getAmount() > 1) { + itemStack.setAmount(itemStack.getAmount() - 1); + return; + } + + event.getEvent().getClickedInventory().clear(event.getEvent().getSlot()); + }); + + // Player closed inventory + onClose(event -> { + if (getInventory().getItem(RIGHT_SLOT) != null) + event.getPlayer().getInventory().addItem(getInventory().getItem(RIGHT_SLOT)); + if (getInventory().getItem(LEFT_SLOT) != null) + event.getPlayer().getInventory().addItem(getInventory().getItem(LEFT_SLOT)); + }); + } + + private boolean handleItem(Player player, ItemStack itemStack) { + if (itemStack == null) { + return false; + } + + ItemStack toHandle = itemStack.clone(); + toHandle.setAmount(1); + + NBTItem nbtItem = new NBTItem(toHandle); + + if (!nbtItem.hasKey("book-item") && !nbtItem.hasKey("dust")) { + instance.getAction().perform(player, "alchemist.not-interested"); + return false; + } + + // Both slots occupied + if (getInventory().getItem(LEFT_SLOT) != null && getInventory().getItem(RIGHT_SLOT) != null) { + instance.getAction().perform(player, "alchemist.max-two-items"); + return false; + } + + int successRate = nbtItem.getInteger("success-rate"); + + // Both slots empty + if (getInventory().getItem(LEFT_SLOT) == null && getInventory().getItem(RIGHT_SLOT) == null) { + if (nbtItem.hasKey("book-item")) { + Enchant enchant = instance.getEnchantManager().getValue(nbtItem.getString("enchant")).orElseThrow(() -> new IllegalStateException("Book without enchant!")); + int level = nbtItem.getInteger("level"); + + if (enchant.getMaxLevel() == level) { + instance.getAction().perform(player, "alchemist.max-level-book"); + return false; + } + } else { + Group group = instance.getGroupManager().getValue(nbtItem.getString("group")).orElseThrow(() -> new IllegalStateException("Dust without group!")); + + if (group.getOrder() == instance.getGroupManager().getValues().stream().mapToInt(Group::getOrder).max().orElse(0) || successRate == 100) { + instance.getAction().perform(player, "alchemist." + (successRate == 100 ? "max-percentage-dust" : "highest-group-dust")); + return false; + } + } + + getInventory().setItem(LEFT_SLOT, toHandle); + return true; + } + + NBTItem other = new NBTItem(getInventory().getItem(getInventory().getItem(LEFT_SLOT) == null ? RIGHT_SLOT : LEFT_SLOT)); + int emptySlot = getInventory().getItem(LEFT_SLOT) == null ? LEFT_SLOT : RIGHT_SLOT; + + if (other.hasKey("book-item")) { + if (!nbtItem.getString("enchant").equals(other.getString("enchant"))) { + instance.getAction().perform(player, "alchemist.different-enchantment"); + return false; + } + + if (!nbtItem.getInteger("level").equals(other.getInteger("level"))) { + instance.getAction().perform(player, "alchemist.different-levels"); + return false; + } + } else { + if (!nbtItem.getString("group").equals(other.getString("group"))) { + instance.getAction().perform(player, "alchemist.different-groups"); + return false; + } + + if (successRate == 100) { + instance.getAction().perform(player, "alchemist.max-percentage-dust"); + return false; + } + } + + getInventory().setItem(emptySlot, toHandle); + updateSlots(); + return true; + } + + private void updateSlots() { + if (getInventory().getItem(LEFT_SLOT) == null || getInventory().getItem(RIGHT_SLOT) == null) { + addItem(ACCEPT_SLOT, ACCEPT_ITEM); + addItem(PREVIEW_SLOT, PREVIEW_ITEM); + return; + } + + NBTItem leftItem = new NBTItem(getInventory().getItem(LEFT_SLOT)); + NBTItem rightItem = new NBTItem(getInventory().getItem(RIGHT_SLOT)); + int ecoCost; + int expCost; + + if (leftItem.hasKey("book-item")) { + int level = leftItem.getInteger("level"); + Enchant enchant = instance.getEnchantManager().getValue(leftItem.getString("enchant")).orElseThrow(() -> new IllegalStateException("Book without enchant!")); + int leftSuccess = leftItem.getInteger("success-rate"); + int rightSuccess = rightItem.getInteger("success-rate"); + int leftDestroy = leftItem.getInteger("destroy-rate"); + int rightDestroy = rightItem.getInteger("destroy-rate"); + + Placeholder[] placeholders = new Placeholder[]{ + of("left_success_rate", leftSuccess), + of("right_success_rate", rightSuccess), + of("left_destroy_rate", leftDestroy), + of("right_destroy_rate", rightDestroy), + of("max_destroy_rate", Math.max(leftDestroy, rightDestroy)), + of("min_destroy_rate", Math.min(leftDestroy, rightDestroy)), + of("max_success_rate", Math.max(leftSuccess, rightSuccess)), + of("min_success_rate", Math.min(leftSuccess, rightSuccess)) + }; + + int successRate = getFromFormula("book.success-rate-formula", placeholders); + int destroyRate = getFromFormula("book.destroy-rate-formula", placeholders); + + Placeholder[] costPlaceholders = new Placeholder[]{ + of("group_order_index", enchant.getGroup().getOrder()), + of("final_success_rate", successRate), + of("final_destroy_rate", destroyRate), + }; + + ecoCost = getFromFormula("book.eco-cost-formula", costPlaceholders); + expCost = getFromFormula("book.exp-cost-formula", costPlaceholders); + + getInventory().setItem(PREVIEW_SLOT, enchant.getBook().get(enchant, level + 1, successRate, destroyRate)); + } else { + Group group = instance.getGroupManager().getValue(leftItem.getString("group")).orElseThrow(() -> new IllegalStateException("Dust without group!")); + + Placeholder[] placeholders = new Placeholder[]{ + of("left_percentage", leftItem.getInteger("percentage")), + of("right_percentage", rightItem.getInteger("percentage")) + }; + + int successRate = getFromFormula("dust.percentage-formula", placeholders); + + Placeholder[] costPlaceholders = new Placeholder[]{ + of("group_order_index", group.getOrder()), + of("final_success_rate", successRate), + }; + + ecoCost = getFromFormula("dust.eco-cost-formula", costPlaceholders); + expCost = getFromFormula("dust.exp-cost-formula", costPlaceholders); + + Group newGroup = instance.getGroupManager().getValues().stream() + .filter(s -> s.getOrder() == group.getOrder() + 1) + .findFirst() + .orElseThrow(() -> new IllegalStateException("No group higher than: " + group.getIdentifier())); + + getInventory().setItem(PREVIEW_SLOT, instance.getSpecialItems().getDust(newGroup, "magic", successRate, true)); + } + + addItem(ACCEPT_SLOT, new ItemBuilder(config.getConfigurationSection("contents.accept-after"), + of("eco_cost", ecoCost), + of("exp_cost", expCost) + ).build(), event -> { + if (!instance.getEconomy().has(event.getPlayer(), ecoCost) || getExp(event.getPlayer()) < expCost) { + instance.getAction().perform(event.getPlayer(), "alchemist.cannot-afford"); + return; + } + + instance.getEconomy().withdrawPlayer(event.getPlayer(), ecoCost); + changeExp(event.getPlayer(), -expCost); + instance.getAction().perform(event.getPlayer(), "alchemist.success", of("eco_cost", ecoCost), of("exp_cost", expCost)); + + event.getPlayer().getInventory().addItem(getInventory().getItem(PREVIEW_SLOT)); + clear(RIGHT_SLOT); + clear(LEFT_SLOT); + event.getPlayer().closeInventory(); + }); + } + + private int getFromFormula(String path, Placeholder... placeholders) { + String toTest = config.getString(path); + + for (Placeholder placeholder : placeholders) + toTest = toTest.replace(placeholder.getPlaceholder(), placeholder.getToReplace().toString()); + + return (int) GeneralUtils.parseJS(toTest, "alchemist expression", 0); } } diff --git a/core/src/main/java/com/songoda/epicenchants/menus/EnchanterMenu.java b/core/src/main/java/com/songoda/epicenchants/menus/EnchanterMenu.java index 15d0a3d..0a67d26 100644 --- a/core/src/main/java/com/songoda/epicenchants/menus/EnchanterMenu.java +++ b/core/src/main/java/com/songoda/epicenchants/menus/EnchanterMenu.java @@ -25,9 +25,9 @@ public class EnchanterMenu extends FastInv { .map(s -> "contents." + s) .map(config::getConfigurationSection) .forEach(section -> { - double expCost = section.getDouble("exp-cost"); - double ecoCost = section.getDouble("eco-cost"); - double xpLeft = expCost - player.getLevel() < 0 ? 0 : expCost - player.getLevel(); + int expCost = section.getInt("exp-cost"); + int ecoCost = section.getInt("eco-cost"); + int xpLeft = expCost - player.getLevel() < 0 ? 0 : expCost - player.getLevel(); double ecoLeft = ecoCost - instance.getEconomy().getBalance(player) < 0 ? 0 : ecoCost - instance.getEconomy().getBalance(player); Group group = instance.getGroupManager().getValue(section.getString("group").toUpperCase()) .orElseThrow(() -> new IllegalArgumentException("Invalid group set in enchanter: " + section.getString("group"))); @@ -50,7 +50,7 @@ public class EnchanterMenu extends FastInv { of("eco_cost", ecoCost), of("exp_cost", expCost)); - changeExp(player, (int) -expCost); + changeExp(player, -expCost); player.getInventory().addItem(instance.getSpecialItems().getMysteryBook(group)); }); }); diff --git a/core/src/main/java/com/songoda/epicenchants/menus/MainInfoMenu.java b/core/src/main/java/com/songoda/epicenchants/menus/MainInfoMenu.java index a5e4337..a61c631 100644 --- a/core/src/main/java/com/songoda/epicenchants/menus/MainInfoMenu.java +++ b/core/src/main/java/com/songoda/epicenchants/menus/MainInfoMenu.java @@ -5,10 +5,11 @@ import com.songoda.epicenchants.objects.Group; import com.songoda.epicenchants.utils.objects.FastInv; import com.songoda.epicenchants.utils.objects.ItemBuilder; import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.event.Listener; import static com.songoda.epicenchants.utils.single.GeneralUtils.color; -public class MainInfoMenu extends FastInv { +public class MainInfoMenu extends FastInv implements Listener { public MainInfoMenu(EpicEnchants instance, FileConfiguration config) { super(config.getInt("rows") * 9, color(config.getString("title"))); @@ -25,4 +26,5 @@ public class MainInfoMenu extends FastInv { }); } + } diff --git a/core/src/main/java/com/songoda/epicenchants/menus/TinkererMenu.java b/core/src/main/java/com/songoda/epicenchants/menus/TinkererMenu.java index a9c2826..bb49f05 100644 --- a/core/src/main/java/com/songoda/epicenchants/menus/TinkererMenu.java +++ b/core/src/main/java/com/songoda/epicenchants/menus/TinkererMenu.java @@ -51,9 +51,9 @@ public class TinkererMenu extends FastInv { if (section.getName().equalsIgnoreCase("accept-left") || section.getName().equalsIgnoreCase("accept-right")) { slotMap.values().stream().map(slot -> getInventory().getItem(slot)).filter(Objects::nonNull).forEach(event.getPlayer().getInventory()::addItem); slotMap.keySet().forEach(slot -> getInventory().clear(slot)); + accepted.set(true); event.getPlayer().closeInventory(); instance.getAction().perform(event.getPlayer(), "tinkerer.accepted"); - accepted.set(true); return; } @@ -105,7 +105,7 @@ public class TinkererMenu extends FastInv { // Player clicked an item in tinkerer onClick(event -> { - if (event.getEvent().getClickedInventory() == null) { + if (event.getEvent().getClickedInventory() == null && event.getInventory().equals(this)) { return; } diff --git a/core/src/main/java/com/songoda/epicenchants/objects/Condition.java b/core/src/main/java/com/songoda/epicenchants/objects/Condition.java index 859836d..a99a5a5 100644 --- a/core/src/main/java/com/songoda/epicenchants/objects/Condition.java +++ b/core/src/main/java/com/songoda/epicenchants/objects/Condition.java @@ -1,17 +1,13 @@ package com.songoda.epicenchants.objects; +import com.songoda.epicenchants.utils.single.GeneralUtils; import com.songoda.epicenchants.utils.single.Placeholders; -import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.event.Event; import org.jetbrains.annotations.Nullable; -import javax.script.ScriptEngine; -import javax.script.ScriptEngineManager; -import javax.script.ScriptException; - public class Condition { private final String string; @@ -28,17 +24,8 @@ public class Condition { return true; } - ScriptEngine scriptEngine = new ScriptEngineManager().getEngineByName("JavaScript"); String toValidate = ChatColor.stripColor(Placeholders.setPlaceholders(string, user, attacker, level, event)); - System.out.println("Verifying: " + toValidate); - - try { - return Boolean.parseBoolean(scriptEngine.eval(toValidate).toString()); - } catch (ScriptException ignore) { - Bukkit.getLogger().warning("[EpicEnchants] One of your condition expressions is not properly formatted."); - Bukkit.getLogger().warning(toValidate); - return def; - } + return (boolean) GeneralUtils.parseJS(toValidate, "condition", def); } } diff --git a/core/src/main/java/com/songoda/epicenchants/objects/Group.java b/core/src/main/java/com/songoda/epicenchants/objects/Group.java index 4c554a9..f21eabc 100644 --- a/core/src/main/java/com/songoda/epicenchants/objects/Group.java +++ b/core/src/main/java/com/songoda/epicenchants/objects/Group.java @@ -14,4 +14,5 @@ public class Group { private BookItem bookItem; private int destroyRateMin, destroyRateMax, successRateMin, successRateMax; private int tinkererExp; + private int order; } diff --git a/core/src/main/java/com/songoda/epicenchants/objects/LeveledModifier.java b/core/src/main/java/com/songoda/epicenchants/objects/LeveledModifier.java index dca2243..d09bcad 100644 --- a/core/src/main/java/com/songoda/epicenchants/objects/LeveledModifier.java +++ b/core/src/main/java/com/songoda/epicenchants/objects/LeveledModifier.java @@ -1,14 +1,10 @@ package com.songoda.epicenchants.objects; +import com.songoda.epicenchants.utils.single.GeneralUtils; import com.songoda.epicenchants.utils.single.Placeholders; -import org.bukkit.Bukkit; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; -import javax.script.ScriptEngine; -import javax.script.ScriptEngineManager; -import javax.script.ScriptException; - public class LeveledModifier { private final String string; @@ -29,15 +25,8 @@ public class LeveledModifier { return Integer.MAX_VALUE; } - ScriptEngine scriptEngine = new ScriptEngineManager().getEngineByName("JavaScript"); String toTest = Placeholders.setPlaceholders(string, user, opponent, level); - try { - return Double.parseDouble(scriptEngine.eval(toTest).toString()); - } catch (ScriptException | NumberFormatException e) { - Bukkit.getLogger().warning("[EpicEnchants] One of your math expressions is not properly formatted."); - Bukkit.getLogger().warning(toTest); - return def; - } + return (double) GeneralUtils.parseJS(toTest, "LeveledModifier", def); } } diff --git a/core/src/main/java/com/songoda/epicenchants/utils/EnchantUtils.java b/core/src/main/java/com/songoda/epicenchants/utils/EnchantUtils.java index 2db1e8b..3d83250 100644 --- a/core/src/main/java/com/songoda/epicenchants/utils/EnchantUtils.java +++ b/core/src/main/java/com/songoda/epicenchants/utils/EnchantUtils.java @@ -48,7 +48,7 @@ public class EnchantUtils { return Pair.of(itemStack, MAXED_OUT); } - if (currentEnchantMap.entrySet().stream().anyMatch(entry -> entry.getKey().equals(enchant) && entry.getValue() == level)) { + if (currentEnchantMap.entrySet().stream().anyMatch(entry -> entry.getKey().equals(enchant) && entry.getValue() >= level)) { return Pair.of(itemStack, ALREADY_APPLIED); } @@ -110,7 +110,7 @@ public class EnchantUtils { public void handlePlayer(@NotNull Player player, @Nullable LivingEntity opponent, Event event, TriggerType triggerType) { List stacks = new ArrayList<>(Arrays.asList(player.getInventory().getArmorContents())); - stacks.add(player.getItemInHand()); + stacks.add(GeneralUtils.getHeldItem(player, event)); stacks.removeIf(Objects::isNull); if (triggerType == HELD_ITEM) { diff --git a/core/src/main/java/com/songoda/epicenchants/utils/SpecialItems.java b/core/src/main/java/com/songoda/epicenchants/utils/SpecialItems.java index 3c572a4..ea4a10f 100644 --- a/core/src/main/java/com/songoda/epicenchants/utils/SpecialItems.java +++ b/core/src/main/java/com/songoda/epicenchants/utils/SpecialItems.java @@ -123,6 +123,6 @@ public class SpecialItems { } public String getWhiteScrollLore() { - return color(instance.getFileManager().getConfiguration("special-items").getConfigurationSection("white-scroll").getString("format")); + return color(instance.getFileManager().getConfiguration("items/special-items").getString("white-scroll.format")); } } diff --git a/core/src/main/java/com/songoda/epicenchants/utils/objects/FastInv.java b/core/src/main/java/com/songoda/epicenchants/utils/objects/FastInv.java index c595de5..eedd16b 100644 --- a/core/src/main/java/com/songoda/epicenchants/utils/objects/FastInv.java +++ b/core/src/main/java/com/songoda/epicenchants/utils/objects/FastInv.java @@ -31,15 +31,6 @@ public class FastInv implements InventoryHolder { private final Map itemListeners = new HashMap<>(); private final Set tasks = new HashSet<>(); - /** - * Create a new FastInv with a custom size. - * - * @param size The size of the menus. - */ - public FastInv(int size) { - this(size, InventoryType.CHEST.getDefaultTitle()); - } - /** * Create a new FastInv with a custom size and title. * @@ -50,26 +41,6 @@ public class FastInv implements InventoryHolder { this(size, InventoryType.CHEST, title); } - /** - * Create a new FastInv with a custom type. - * - * @param type The type of the menus. - */ - public FastInv(InventoryType type) { - this(type, type.getDefaultTitle()); - } - - /** - * Create a new FastInv with a custom type and title. - * - * @param type The type of the menus. - * @param title The title of the menus. - * @throws IllegalStateException if FastInv is not init with FastInv.init(Plugin plugin) - */ - public FastInv(InventoryType type, String title) { - this(0, type, title); - } - private FastInv(int size, InventoryType type, String title) { if (plugin == null) { throw new IllegalStateException("FastInv is not initialised"); @@ -249,6 +220,16 @@ public class FastInv implements InventoryHolder { return addItem(slots, item, null); } + /** + * Clear a spot in the inventory. + * + * @param slot The slot to clear. + * @return This FastInv instance, for chaining. + */ + public FastInv clear(int slot) { + return addItem(slot, null); + } + /** * Add an {@link ItemStack} to the menus on the edges. * @@ -350,19 +331,6 @@ public class FastInv implements InventoryHolder { Bukkit.getScheduler().runTask(plugin, () -> player.openInventory(inventory)); } - /** - * Open the menus to players. - * - * @param players The players to open the menu. - */ - public void open(Player... players) { - Bukkit.getScheduler().runTask(plugin, () -> { - for (Player p : players) { - p.openInventory(inventory); - } - }); - } - /** * Cancel all tasks. */ diff --git a/core/src/main/java/com/songoda/epicenchants/utils/single/ConfigParser.java b/core/src/main/java/com/songoda/epicenchants/utils/single/ConfigParser.java index 0ea20f4..ccdcbd7 100644 --- a/core/src/main/java/com/songoda/epicenchants/utils/single/ConfigParser.java +++ b/core/src/main/java/com/songoda/epicenchants/utils/single/ConfigParser.java @@ -3,10 +3,7 @@ package com.songoda.epicenchants.utils.single; import com.songoda.epicenchants.EpicEnchants; import com.songoda.epicenchants.effect.EffectManager; import com.songoda.epicenchants.enums.TriggerType; -import com.songoda.epicenchants.objects.BookItem; -import com.songoda.epicenchants.objects.Enchant; -import com.songoda.epicenchants.objects.Group; -import com.songoda.epicenchants.objects.LeveledModifier; +import com.songoda.epicenchants.objects.*; import com.songoda.epicenchants.utils.objects.ItemBuilder; import com.songoda.epicenchants.wrappers.EnchantmentWrapper; import com.songoda.epicenchants.wrappers.MobWrapper; @@ -16,10 +13,7 @@ import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.enchantments.Enchantment; import org.bukkit.entity.EntityType; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.Optional; +import java.util.*; import java.util.stream.Collectors; import static com.songoda.epicenchants.utils.single.GeneralUtils.color; @@ -86,6 +80,7 @@ public class ConfigParser { public static Group parseGroup(EpicEnchants instance, ConfigurationSection section) { return section != null ? Group.builder() + .order(section.getInt("order")) .identifier(section.getName()) .name(color(section.getString("group-name"))) .format(section.getString("group-lore-format")) diff --git a/core/src/main/java/com/songoda/epicenchants/utils/single/GeneralUtils.java b/core/src/main/java/com/songoda/epicenchants/utils/single/GeneralUtils.java index 7a2d14e..036ce98 100644 --- a/core/src/main/java/com/songoda/epicenchants/utils/single/GeneralUtils.java +++ b/core/src/main/java/com/songoda/epicenchants/utils/single/GeneralUtils.java @@ -3,15 +3,25 @@ package com.songoda.epicenchants.utils.single; import com.songoda.epicenchants.enums.EnchantResult; import com.songoda.epicenchants.enums.TriggerType; import org.apache.commons.lang.StringUtils; +import org.bukkit.Bukkit; import org.bukkit.ChatColor; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.entity.Player; +import org.bukkit.event.Event; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; -import java.util.Arrays; -import java.util.List; -import java.util.Set; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import javax.script.ScriptException; +import java.util.*; import java.util.concurrent.ThreadLocalRandom; import java.util.stream.Collectors; public class GeneralUtils { + private static final ScriptEngine SCRIPT_ENGINE = new ScriptEngineManager().getEngineByName("JavaScript"); + public static boolean chance(int chance) { return chance((double) chance); } @@ -24,6 +34,10 @@ public class GeneralUtils { return format(input, "", null); } + public static List getString(ConfigurationSection section, String path) { + return section.isList(path) ? section.getStringList(path) : Collections.singletonList(section.getString(path)); + } + public static String format(String input, String placeholder, Object toReplace) { return ChatColor.translateAlternateColorCodes('&', input).replaceAll(placeholder, toReplace == null ? "" : toReplace.toString()); } @@ -47,11 +61,31 @@ public class GeneralUtils { return Arrays.stream(string.split(",")).filter(StringUtils::isNumeric).mapToInt(Integer::parseInt).toArray(); } - public static List getSlotsList(String string) { - return Arrays.stream(string.split(",")).filter(StringUtils::isNumeric).mapToInt(Integer::parseInt).boxed().collect(Collectors.toList()); - } - public static Set parseTrigger(String triggers) { return Arrays.stream(triggers.replaceAll("\\s+", "").split(",")).map(TriggerType::valueOf).collect(Collectors.toSet()); } + + public static ItemStack getHeldItem(Player player, Event event) { + int slot = player.getInventory().getHeldItemSlot(); + + try { + if (event instanceof PlayerInteractEvent && ((PlayerInteractEvent) event).getHand() == EquipmentSlot.OFF_HAND) { + slot = 40; + } + } catch (NoSuchMethodError ignore) { + } + + return player.getInventory().getItem(slot); + } + + public static Object parseJS(String toParse, String type, Object def) { + + try { + return SCRIPT_ENGINE.eval(toParse); + } catch (ScriptException | NumberFormatException e) { + Bukkit.getLogger().warning("[EpicEnchants] One of your " + type + " expressions is not properly formatted."); + Bukkit.getLogger().warning(toParse); + return def; + } + } } diff --git a/core/src/main/java/com/songoda/epicenchants/utils/single/Placeholders.java b/core/src/main/java/com/songoda/epicenchants/utils/single/Placeholders.java index 216d992..81e31f0 100644 --- a/core/src/main/java/com/songoda/epicenchants/utils/single/Placeholders.java +++ b/core/src/main/java/com/songoda/epicenchants/utils/single/Placeholders.java @@ -2,6 +2,7 @@ package com.songoda.epicenchants.utils.single; import me.clip.placeholderapi.PlaceholderAPI; import org.bukkit.Bukkit; +import org.bukkit.Material; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.event.Event; @@ -9,13 +10,19 @@ import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.player.PlayerInteractEntityEvent; import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.ItemStack; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; +import java.util.*; +import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.atomic.AtomicReference; import java.util.function.BiFunction; +import java.util.function.Consumer; import java.util.function.Function; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import static com.songoda.epicenchants.utils.single.GeneralUtils.getHeldItem; public class Placeholders { @@ -25,29 +32,57 @@ public class Placeholders { return ((BlockBreakEvent) event).getBlock().getType().toString(); } else if (event instanceof BlockPlaceEvent) { return ((BlockPlaceEvent) event).getBlockPlaced().getType().toString(); + } else if (event instanceof PlayerInteractEvent && ((PlayerInteractEvent) event).hasBlock()) { + return ((PlayerInteractEvent) event).getClickedBlock().getType().toString(); } else return "N/A"; }); put("{clicked_type}", event -> { - if (event instanceof PlayerInteractEvent && ((PlayerInteractEvent) event).hasBlock()) { - return ((PlayerInteractEvent) event).getClickedBlock().getType().toString(); - } else if (event instanceof PlayerInteractEntityEvent) { + if (event instanceof PlayerInteractEntityEvent) { return ((PlayerInteractEntityEvent) event).getRightClicked().getType().toString(); } else return "N/A"; }); }}; private static final Map> PLAYER_FUNCTIONS = new HashMap>() {{ + put("{user_name}", (user, opponent) -> user.getName()); + put("{opponent_name}", (user, opponent) -> opponent == null ? "" : opponent.getName()); + put("{user_health}", (user, opponent) -> user.getHealth()); - put("{attacker_health}", (user, opponent) -> opponent == null ? -1 : opponent.getHealth()); + put("{opponent_health}", (user, opponent) -> opponent == null ? -1 : opponent.getHealth()); + put("{user_food}", (user, opponent) -> user.getFoodLevel()); - put("{attacker_food}", (user, opponent) -> opponent instanceof Player ? ((Player) opponent).getFoodLevel() : 0); + put("{opponent_food}", (user, opponent) -> opponent instanceof Player ? ((Player) opponent).getFoodLevel() : 0); + put("{user_is_sneaking}", (user, opponent) -> user.isSneaking()); - put("{attacker_is_sneaking}", (user, opponent) -> opponent instanceof Player && ((Player) opponent).isSneaking()); + put("{opponent_is_sneaking}", (user, opponent) -> opponent instanceof Player && ((Player) opponent).isSneaking()); + + put("{user_holding}", (user, opponent) -> Optional.ofNullable(getHeldItem(user, null)).map(ItemStack::getType).orElse(Material.AIR)); + put("{opponent_holding}", (user, opponent) -> opponent instanceof Player ? Optional.ofNullable(getHeldItem((Player) opponent, null)).map(ItemStack::getType).orElse(Material.AIR) : "N/A"); + + put("{user_is_swimming}", (user, opponent) -> user.getLocation().getBlock().isLiquid()); + put("{opponent_is_swimming}", (user, opponent) -> opponent != null && opponent.getLocation().getBlock().isLiquid()); + put("{world}", (user, opponent) -> user.getWorld().getName()); put("{players_near}", (user, opponent) -> user.getNearbyEntities(4, 4, 4).size()); put("{user_on_fire}", (user, opponent) -> user.getFireTicks() != 0); - put("{attacker_on_fire}", (user, opponent) -> opponent != null && opponent.getFireTicks() != 0); + put("{opponent_on_fire}", (user, opponent) -> opponent != null && opponent.getFireTicks() != 0); + }}; + + private static final Set>> REGEX_CONSUMERS = new HashSet>>() {{ + add(reference -> { + Pattern pattern = Pattern.compile("\\{random\\((.*)\\)}"); + Matcher matcher = pattern.matcher(reference.get()); + + if (!matcher.find()) { + return; + } + + Map args = Arrays.stream(matcher.group(1).replaceAll("/\\s/g", "").split(",")) + .collect(Collectors.toMap(s -> s.split("=")[0], s -> Integer.parseInt(s.split("=")[1]))); + + reference.getAndUpdate(s -> s.replaceAll(pattern.pattern(), "" + ThreadLocalRandom.current().nextInt(args.get("low"), args.get("up")))); + }); }}; public static String setPlaceholders(String input, Player user, LivingEntity opponent, int level) { @@ -58,7 +93,8 @@ public class Placeholders { AtomicReference output = new AtomicReference<>(input.replace("{level}", "" + level)); PLAYER_FUNCTIONS.forEach((toReplace, function) -> output.updateAndGet(string -> string.replace(toReplace, function.apply(user, opponent).toString()))); - Optional.ofNullable(event).ifPresent(e -> EVENT_FUNCTIONS.forEach((toReplace, function) -> output.updateAndGet(string -> "'" + string.replace(toReplace, function.apply(e)) + "'"))); + REGEX_CONSUMERS.forEach(consumer -> consumer.accept(output)); + Optional.ofNullable(event).ifPresent(e -> EVENT_FUNCTIONS.forEach((toReplace, function) -> output.updateAndGet(string -> string.replace(toReplace, "'" + function.apply(e) + "'")))); if (Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI")) { output.updateAndGet(string -> PlaceholderAPI.setPlaceholders(user, string)); @@ -70,4 +106,6 @@ public class Placeholders { return output.get(); } + + } diff --git a/core/src/main/java/com/songoda/epicenchants/utils/single/RomanNumber.java b/core/src/main/java/com/songoda/epicenchants/utils/single/RomanNumber.java index 941ccaa..c57e5b4 100644 --- a/core/src/main/java/com/songoda/epicenchants/utils/single/RomanNumber.java +++ b/core/src/main/java/com/songoda/epicenchants/utils/single/RomanNumber.java @@ -25,6 +25,7 @@ public class RomanNumber { return map.get(number); } return map.get(l) + toRoman(number - l); + } } diff --git a/core/src/main/resources/actions.yml b/core/src/main/resources/actions.yml index ab67284..7a968b9 100644 --- a/core/src/main/resources/actions.yml +++ b/core/src/main/resources/actions.yml @@ -1,5 +1,4 @@ general: - prefix: "&8[&6EpicEnchants&8]" no-permission: "&cYou do not have permission to do that." command: @@ -15,23 +14,35 @@ command: reload: "&6Configuration files reload" enchanter: - cannot-afford: "&cYou cannot afford this purchase." + cannot-afford: "&c&l(!) &r&cYou cannot afford this purchase." success: "&7Purchased {group_color}{group_name} &7book for &f{exp_cost} EXP&7." tinkerer: open: "&eTrading with the tinkerer." - cancelled: "&cCancelled." + cancelled: "&c&l(!) &r&cCancelled." accepted: "&aAccepted" no-items: "&c&l(!) &r&cThe tinkerer is not interested in any of your items!" deposited-all: "&a&l(!) &r&aDeposited {amount} items." +alchemist: + max-two-items: "&c&l(!) &r&cYou may only combine 2 items at once." + not-interested: "&c&l(!) &r&cThe alchemist is not interested in any of your items!" + max-level-book: "&c&l(!) &r&cThe alchemist cannot combine max level books." + max-percentage-dust: "&c&l(!) &r&cThe alchemist cannot combine 100% success rate dusts." + highest-group-dust: "&c&l(!) &r&cThe alchemist can't accept dust of the highest tier." + different-enchantment: "&c&l(!) &r&cThe alchemist can only combine books with the same enchantment." + different-levels: "&c&l(!) &r&cThe alchemist can only combine books of the same level." + different-groups: "&c&l(!) &r&cThe alchemist can only combine dusts of the same group." + cannot-afford: "&c&l(!) You cannot afford this exchange." + success: "&7Exchanged for &f{exp_cost} EXP&7." + enchants: invalid-material: "&cYou can not apply &6{enchant} &cto that item." broken-failure: "&6{enchant} &cfailed to apply and broke your item..." success: "&aYou have success fully applied &6{enchant}." conflict: "&cYou cannot apply this enchant as it conflicts with another enchant." maxed-out: "&cYou already have that enchant maxed on this item." - already-applied: "&cYou already have that enchant with that level applied on this item." + already-applied: "&cYou already have that enchant applied on this item." protected: "&aYour book would have broken your item, luckily it was protected!" book: diff --git a/core/src/main/resources/groups.yml b/core/src/main/resources/groups.yml index 229289e..51f5675 100644 --- a/core/src/main/resources/groups.yml +++ b/core/src/main/resources/groups.yml @@ -1,5 +1,6 @@ groups: SIMPLE: + order: 0 group-color: "&f" group-name: "Simple" group-lore-format: "{group_color}{enchant} {level}" @@ -18,6 +19,7 @@ groups: - "&7{item_group} Enchantment" - "&7Drag and drop to enchant." UNIQUE: + order: 1 group-color: "&a" group-name: "Unique" group-lore-format: "{group_color}{enchant} {level}" @@ -36,6 +38,7 @@ groups: - "&7{item_group} Enchantment" - "&7Drag and drop to enchant." ELITE: + order: 2 group-color: "&b" group-name: "Elite" group-lore-format: "{group_color}{enchant} {level}" @@ -54,6 +57,7 @@ groups: - "&7{item_group} Enchantment" - "&7Drag and drop to enchant." ULTIMATE: + order: 3 group-color: "&e" group-name: "Ultimate" group-lore-format: "{group_color}{enchant} {level}" @@ -72,6 +76,7 @@ groups: - "&7{item_group} Enchantment" - "&7Drag and drop to enchant." LEGENDARY: + order: 4 group-color: "&6" group-name: "Legendary" group-lore-format: "{group_color}{enchant} {level}" diff --git a/core/src/main/resources/version-dependent/legacy/items/special-items.yml b/core/src/main/resources/version-dependent/legacy/items/special-items.yml index 5b0d15c..0e95f02 100644 --- a/core/src/main/resources/version-dependent/legacy/items/special-items.yml +++ b/core/src/main/resources/version-dependent/legacy/items/special-items.yml @@ -8,7 +8,7 @@ white-scroll: - "&ePlace scroll on item to apply." black-scroll: - material: INK_SACK + material: INC_SAC display-name: "&f&lBlack Scroll" lore: - "&7Removes a random enchantment" diff --git a/core/src/main/resources/version-dependent/legacy/menus/alchemist-menu.yml b/core/src/main/resources/version-dependent/legacy/menus/alchemist-menu.yml new file mode 100644 index 0000000..38d25b5 --- /dev/null +++ b/core/src/main/resources/version-dependent/legacy/menus/alchemist-menu.yml @@ -0,0 +1,53 @@ +title: "Alchemist" +rows: 3 + +fill: + material: STAINED_GLASS_PANE + data: 7 + display-name: "&r" + +left-slot: 3 +right-slot: 5 +preview-slot: 13 +accept-slot: 22 + +book: + success-rate-formula: "({left_success_rate} + {right_success_rate}) / 4" + destroy-rate-formula: "({left_destroy_rate} + {right_destroy_rate}) / 4 + {max_destroy_rate}" + eco-cost-formula: "0" + exp-cost-formula: "({group_order_index} + 1) * {final_success_rate}" + +dust: + percentage-formula: "({left_percentage} + {right_percentage}) / 2" + eco-cost-formula: "0" + exp-cost-formula: "({group_order_index} + 1) * {final_success_rate}" + +contents: + preview: + material: STAINED_GLASS_PANE + display-name: "&eITEM PREVIEW" + lore: + - "&7A preview of the item that you" + - "&7will receive from the alchemist" + - "&7will be displayed here." + accept-before: + material: STAINED_GLASS_PANE + data: 14 + display-name: "&8[&eThe Alchemist&8]" + lore: + - "&7You will exchange" + - "" + - "&8- &f2x enchantment books" + - "&7(of the same type and level) &ffor" + - "&fthe same enchantment book" + - "&7(with a higher success rate)" + accept-after: + material: STAINED_GLASS_PANE + data: 5 + display-name: "&eClick to confirm" + lore: + - "&cCost: {exp_cost} EXP" + - "" + - "&7Click to confirm the exchange" + - "&7after which you will receive" + - "&7the item displayed above." diff --git a/core/src/main/resources/version-dependent/master/menus/alchemist-menu.yml b/core/src/main/resources/version-dependent/master/menus/alchemist-menu.yml new file mode 100644 index 0000000..df7c805 --- /dev/null +++ b/core/src/main/resources/version-dependent/master/menus/alchemist-menu.yml @@ -0,0 +1,50 @@ +title: "Alchemist" +rows: 3 + +fill: + material: GRAY_STAINED_GLASS_PANE + display-name: "&r" + +left-slot: 3 +right-slot: 5 +preview-slot: 13 +accept-slot: 22 + +book: + success-rate-formula: "({left_success_rate} + {right_success_rate}) / 4" + destroy-rate-formula: "({left_destroy_rate} + {right_destroy_rate}) / 4 + {max_destroy_rate}" + eco-cost-formula: "0" + exp-cost-formula: "({group_order_index} + 1) * {final_success_rate}" + +dust: + percentage-formula: "({left_percentage} + {right_percentage}) / 2" + eco-cost-formula: "0" + exp-cost-formula: "({group_order_index} + 1) * {final_success_rate}" + +contents: + preview: + material: WHITE_STAINED_GLASS_PANE + display-name: "&eITEM PREVIEW" + lore: + - "&7A preview of the item that you" + - "&7will receive from the alchemist" + - "&7will be displayed here." + accept-before: + material: RED_STAINED_GLASS_PANE + display-name: "&8[&eThe Alchemist&8]" + lore: + - "&7You will exchange" + - "" + - "&8- &f2x enchantment books" + - "&7(of the same type and level) &ffor" + - "&fthe same enchantment book" + - "&7(with a higher success rate)" + accept-after: + material: LIME_STAINED_GLASS_PANE + display-name: "&eClick to confirm" + lore: + - "&cCost: {exp_cost} EXP" + - "" + - "&7Click to confirm the exchange" + - "&7after which you will receive" + - "&7the item displayed above."