- 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
This commit is contained in:
GB6 2019-04-03 15:58:13 +02:00
parent f8bd9e31eb
commit 817aa2cb8d
35 changed files with 637 additions and 176 deletions

View File

@ -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<String> getMessage(String node, Placeholder... placeholders) {
List<String> 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");
}
}

View File

@ -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);
}
}

View File

@ -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] <level> <success-rate> <destroy-rate>
@Subcommand("give book")
@CommandCompletion("@players @enchants @levels @increment @increment")
@ -68,10 +52,10 @@ public class EnchantCommand extends BaseCommand {
//ee give item [giveType] [player] <amount> <success-rate>
@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":

View File

@ -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<TriggerType> triggerTypes;
private final Set<EffectExecutor> 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);

View File

@ -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));
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}
}

View File

@ -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")));
}
}

View File

@ -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);
}
}

View File

@ -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) {

View File

@ -1,5 +1,5 @@
package com.songoda.epicenchants.enums;
public enum GiveType {
WHITE_SCROLL, BLACK_SCROLL, DUST
WHITE_SCROLL, BLACK_SCROLL
}

View File

@ -27,5 +27,6 @@ public enum TriggerType {
BLOCK_BREAK,
REPEATING,
RIGHT_CLICK
RIGHT_CLICK,
LEFT_CLICK
}

View File

@ -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);
}
}

View File

@ -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> enchant = instance.getEnchantManager().getRandomEnchant(group);

View File

@ -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());
}
}

View File

@ -24,6 +24,7 @@ public class FileManager extends Manager<String, FileConfiguration> {
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<String, FileConfiguration> {
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() {

View File

@ -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<String> filter = new HashSet<String>() {{
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);
}
}

View File

@ -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));
});
});

View File

@ -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 {
});
}
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -14,4 +14,5 @@ public class Group {
private BookItem bookItem;
private int destroyRateMin, destroyRateMax, successRateMin, successRateMax;
private int tinkererExp;
private int order;
}

View File

@ -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);
}
}

View File

@ -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<ItemStack> 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) {

View File

@ -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"));
}
}

View File

@ -31,15 +31,6 @@ public class FastInv implements InventoryHolder {
private final Map<Integer, FastInvClickListener> itemListeners = new HashMap<>();
private final Set<BukkitTask> 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.
*/

View File

@ -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"))

View File

@ -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<String> 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<Integer> getSlotsList(String string) {
return Arrays.stream(string.split(",")).filter(StringUtils::isNumeric).mapToInt(Integer::parseInt).boxed().collect(Collectors.toList());
}
public static Set<TriggerType> 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;
}
}
}

View File

@ -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<String, BiFunction<Player, LivingEntity, Object>> PLAYER_FUNCTIONS = new HashMap<String, BiFunction<Player, LivingEntity, Object>>() {{
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<Consumer<AtomicReference<String>>> REGEX_CONSUMERS = new HashSet<Consumer<AtomicReference<String>>>() {{
add(reference -> {
Pattern pattern = Pattern.compile("\\{random\\((.*)\\)}");
Matcher matcher = pattern.matcher(reference.get());
if (!matcher.find()) {
return;
}
Map<String, Integer> 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<String> 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();
}
}

View File

@ -25,6 +25,7 @@ public class RomanNumber {
return map.get(number);
}
return map.get(l) + toRoman(number - l);
}
}

View File

@ -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:

View File

@ -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}"

View File

@ -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"

View File

@ -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."

View File

@ -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."