Enchants fixes, PL lang, PAPI support

This commit is contained in:
nulli0n 2023-02-22 23:48:03 +06:00
parent 2e4d9b45b7
commit 92e28f8239
17 changed files with 242 additions and 80 deletions

View File

@ -35,6 +35,10 @@
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>placeholderapi</id>
<url>https://repo.extendedclip.com/content/repositories/placeholderapi/</url>
</repository>
</repositories>
<dependencies>
@ -90,6 +94,12 @@
<artifactId>ProtocolLib</artifactId>
<version>5.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>me.clip</groupId>
<artifactId>placeholderapi</artifactId>
<version>2.11.2</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>

View File

@ -15,6 +15,7 @@ import su.nightexpress.excellentenchants.config.Lang;
import su.nightexpress.excellentenchants.enchantment.EnchantManager;
import su.nightexpress.excellentenchants.enchantment.type.FitItemType;
import su.nightexpress.excellentenchants.hook.HookId;
import su.nightexpress.excellentenchants.hook.impl.PlaceholderHook;
import su.nightexpress.excellentenchants.hook.impl.ProtocolHook;
import su.nightexpress.excellentenchants.nms.EnchantNMS;
import su.nightexpress.excellentenchants.nms.v1_17_R1.V1_17_R1;
@ -62,6 +63,7 @@ public class ExcellentEnchants extends NexPlugin<ExcellentEnchants> {
this.tierManager.shutdown();
this.tierManager = null;
}
PlaceholderHook.shutdown();
}
private boolean setNMS() {
@ -106,6 +108,9 @@ public class ExcellentEnchants extends NexPlugin<ExcellentEnchants> {
Config.ENCHANTMENTS_DISPLAY_MODE.set(1);
}
}
if (Hooks.hasPlaceholderAPI()) {
PlaceholderHook.setup();
}
}
@Override

View File

@ -5,6 +5,7 @@ import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import su.nexmedia.engine.api.config.JOption;
import su.nexmedia.engine.api.config.JYML;
import su.nexmedia.engine.utils.Colorizer;
import su.nexmedia.engine.utils.StringUtil;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.enchantment.type.ObtainType;
@ -30,7 +31,7 @@ public class Config {
Placeholders.URL_WIKI + "Charges-System");
public static final JOption<TreeMap<Double, String>> ENCHANTMENTS_CHARGES_FORMAT = new JOption<TreeMap<Double, String>>("Enchantments.Charges.Format",
(cfg, path, def) -> cfg.getSection(path).stream().collect(Collectors.toMap(k -> StringUtil.getDouble(k, 0), v -> StringUtil.color(cfg.getString(path + "." + v, "")), (o,n) -> n, TreeMap::new)),
(cfg, path, def) -> cfg.getSection(path).stream().collect(Collectors.toMap(k -> StringUtil.getDouble(k, 0), v -> Colorizer.apply(cfg.getString(path + "." + v, "")), (o,n) -> n, TreeMap::new)),
() -> {
TreeMap<Double, String> map = new TreeMap<>();
map.put(0D, "#ff9a9a(" + Placeholders.GENERIC_AMOUNT + "⚡)");

View File

@ -208,7 +208,7 @@ public class EnchantManager extends AbstractManager<ExcellentEnchants> {
Map<ExcellentEnchant, Integer> enchants = EnchantManager.getExcellentEnchantments(item);
int sizeHas = PDCUtil.getInt(item, KEY_LORE_SIZE).orElse(0);
int sizeReal = enchants.size() + enchants.keySet().stream().map(ExcellentEnchant::getDescription).mapToInt(List::size).sum();
int sizeReal = enchants.size();
ItemMeta meta = item.getItemMeta();
if (meta == null) return false;
@ -219,9 +219,12 @@ public class EnchantManager extends AbstractManager<ExcellentEnchants> {
}
if (!meta.hasItemFlag(ItemFlag.HIDE_ENCHANTS)) {
enchants.forEach((enchant, level) -> {
lore.addAll(0, enchant.formatDescription(level));
});
if (Config.ENCHANTMENTS_DESCRIPTION_ENABLED.get()) {
enchants.forEach((enchant, level) -> {
lore.addAll(0, enchant.formatDescription(level));
});
sizeReal += enchants.keySet().stream().map(ExcellentEnchant::getDescription).mapToInt(List::size).sum();
}
enchants.forEach((enchant, level) -> {
lore.add(0, enchant.getNameFormatted(level, getEnchantmentCharges(meta, enchant)));
});

View File

@ -13,15 +13,17 @@ import su.nexmedia.engine.utils.EntityUtil;
import su.nexmedia.engine.utils.NumberUtil;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.api.enchantment.ExcellentEnchant;
import su.nightexpress.excellentenchants.api.enchantment.meta.Chanced;
import su.nightexpress.excellentenchants.api.enchantment.type.PassiveEnchant;
import su.nightexpress.excellentenchants.api.enchantment.util.EnchantPriority;
import su.nightexpress.excellentenchants.enchantment.EnchantManager;
import su.nightexpress.excellentenchants.enchantment.config.EnchantScaler;
import su.nightexpress.excellentenchants.enchantment.impl.meta.ChanceImplementation;
import su.nightexpress.excellentenchants.enchantment.task.AbstractEnchantmentTask;
import java.util.function.UnaryOperator;
public class EnchantRegrowth extends ExcellentEnchant implements PassiveEnchant, ICleanable {
public class EnchantRegrowth extends ExcellentEnchant implements Chanced, PassiveEnchant, ICleanable {
public static final String ID = "regrowth";
@ -35,6 +37,7 @@ public class EnchantRegrowth extends ExcellentEnchant implements PassiveEnchant,
private EnchantScaler healMaxHealth;
private EnchantScaler healAmount;
private ChanceImplementation chanceImplementation;
private Task task;
public EnchantRegrowth(@NotNull ExcellentEnchants plugin) {
@ -45,6 +48,7 @@ public class EnchantRegrowth extends ExcellentEnchant implements PassiveEnchant,
@Override
public void loadConfig() {
super.loadConfig();
this.chanceImplementation = ChanceImplementation.create(this);
this.healInterval = JOption.create("Settings.Heal.Interval", 100,
"How often (in ticks) enchantment will have effect? 1 second = 20 ticks.").read(cfg);
this.healMinHealth = EnchantScaler.read(this, "Settings.Heal.Min_Health", "0.5",
@ -81,6 +85,12 @@ public class EnchantRegrowth extends ExcellentEnchant implements PassiveEnchant,
;
}
@NotNull
@Override
public ChanceImplementation getChanceImplementation() {
return chanceImplementation;
}
@NotNull
@Override
public EnchantmentTarget getItemTarget() {
@ -106,6 +116,7 @@ public class EnchantRegrowth extends ExcellentEnchant implements PassiveEnchant,
@Override
public boolean onTrigger(@NotNull LivingEntity entity, @NotNull ItemStack item, int level) {
if (!this.isAvailableToUse(entity)) return false;
if (!this.checkTriggerChance(level)) return false;
double healthMax = EntityUtil.getAttribute(entity, Attribute.GENERIC_MAX_HEALTH);
double healthHas = entity.getHealth();

View File

@ -1,6 +1,7 @@
package su.nightexpress.excellentenchants.enchantment.impl.bow;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.AbstractArrow;
import org.bukkit.entity.Arrow;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Projectile;
@ -61,6 +62,7 @@ public class EnchantConfusingArrows extends PotionEnchant implements Chanced, Ar
if (!(e.getProjectile() instanceof Arrow arrow)) return false;
if (!this.checkTriggerChance(level)) return false;
arrow.setPickupStatus(AbstractArrow.PickupStatus.DISALLOWED);
return arrow.addCustomEffect(this.createEffect(level), true);
}

View File

@ -4,6 +4,7 @@ import org.bukkit.Particle;
import org.bukkit.block.Block;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.Item;
import org.bukkit.entity.ItemFrame;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Projectile;
import org.bukkit.event.EventHandler;
@ -100,9 +101,9 @@ public class EnchantElectrifiedArrows extends ExcellentEnchant implements Chance
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onItemDamage(EntityDamageByEntityEvent e) {
if (!e.getDamager().hasMetadata(META_NO_ITEM_DAMAGE)) return;
if (!(e.getEntity() instanceof Item item)) return;
e.setCancelled(true);
item.setFireTicks(0);
if (e.getEntity() instanceof Item || e.getEntity() instanceof ItemFrame) {
e.setCancelled(true);
e.getEntity().setFireTicks(0);
}
}
}

View File

@ -2,10 +2,7 @@ package su.nightexpress.excellentenchants.enchantment.impl.bow;
import org.bukkit.World;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Item;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Projectile;
import org.bukkit.entity.*;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
@ -138,8 +135,9 @@ public class EnchantExplosiveArrows extends ExcellentEnchant implements Chanced,
if (e.getCause() != EntityDamageEvent.DamageCause.ENTITY_EXPLOSION) return;
if (this.explosionDamageItems) return;
if (!e.getDamager().hasMetadata(META_EXPLOSION_SOURCE)) return;
if (!(e.getEntity() instanceof Item item)) return;
e.setCancelled(true);
if (e.getEntity() instanceof Item || e.getEntity() instanceof ItemFrame) {
e.setCancelled(true);
}
}
}

View File

@ -1,6 +1,7 @@
package su.nightexpress.excellentenchants.enchantment.impl.bow;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.AbstractArrow;
import org.bukkit.entity.Arrow;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Projectile;
@ -61,6 +62,7 @@ public class EnchantHover extends PotionEnchant implements Chanced, Arrowed, Bow
if (!(e.getProjectile() instanceof Arrow arrow)) return false;
if (!this.checkTriggerChance(level)) return false;
arrow.setPickupStatus(AbstractArrow.PickupStatus.DISALLOWED);
return arrow.addCustomEffect(this.createEffect(level), true);
}

View File

@ -1,6 +1,7 @@
package su.nightexpress.excellentenchants.enchantment.impl.bow;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.AbstractArrow;
import org.bukkit.entity.Arrow;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Projectile;
@ -61,6 +62,7 @@ public class EnchantPoisonedArrows extends PotionEnchant implements Chanced, Arr
if (!(e.getProjectile() instanceof Arrow arrow)) return false;
if (!this.checkTriggerChance(level)) return false;
arrow.setPickupStatus(AbstractArrow.PickupStatus.DISALLOWED);
return arrow.addCustomEffect(this.createEffect(level), true);
}

View File

@ -1,6 +1,7 @@
package su.nightexpress.excellentenchants.enchantment.impl.bow;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.AbstractArrow;
import org.bukkit.entity.Arrow;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Projectile;
@ -61,6 +62,7 @@ public class EnchantWitheredArrows extends PotionEnchant implements Chanced, Arr
if (!(e.getProjectile() instanceof Arrow arrow)) return false;
if (!this.checkTriggerChance(level)) return false;
arrow.setPickupStatus(AbstractArrow.PickupStatus.DISALLOWED);
return arrow.addCustomEffect(this.createEffect(level), true);
}

View File

@ -2,18 +2,27 @@ package su.nightexpress.excellentenchants.enchantment.impl.weapon;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.Particle;
import org.bukkit.block.Skull;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.block.BlockDropItemEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.entity.EntityDeathEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.SkullMeta;
import org.jetbrains.annotations.NotNull;
import su.nexmedia.engine.api.config.JOption;
import su.nexmedia.engine.lang.LangManager;
import su.nexmedia.engine.utils.EffectUtil;
import su.nexmedia.engine.utils.ItemUtil;
import su.nexmedia.engine.utils.PDCUtil;
import su.nexmedia.engine.utils.StringUtil;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.api.enchantment.ExcellentEnchant;
@ -37,8 +46,11 @@ public class EnchantDecapitator extends ExcellentEnchant implements Chanced, Dea
private Map<String, String> headTextures;
private ChanceImplementation chanceImplementation;
private final NamespacedKey skullKey;
public EnchantDecapitator(@NotNull ExcellentEnchants plugin) {
super(plugin, ID, EnchantPriority.MEDIUM);
this.skullKey = new NamespacedKey(plugin, this.getId() + ".entity_type");
}
@Override
@ -132,6 +144,7 @@ public class EnchantDecapitator extends ExcellentEnchant implements Chanced, Dea
meta.setDisplayName(entityName);
item.setItemMeta(meta);
}
PDCUtil.set(item, this.skullKey, entityType.name());
entity.getWorld().dropItemNaturally(entity.getLocation(), item);
@ -140,4 +153,41 @@ public class EnchantDecapitator extends ExcellentEnchant implements Chanced, Dea
}
return true;
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onBlockPlace(BlockPlaceEvent e) {
if (!(e.getBlock().getState() instanceof Skull skull)) return;
ItemStack skullItem = e.getItemInHand();
PDCUtil.getString(skullItem, this.skullKey).ifPresent(type -> {
PDCUtil.set(skull, this.skullKey, type);
skull.update(true);
});
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onBlockPlace(BlockDropItemEvent e) {
if (!(e.getBlockState() instanceof Skull skull)) return;
PDCUtil.getString(skull, this.skullKey).ifPresent(type -> {
String texture = this.headTextures.get(type);
if (texture == null) return;
EntityType entityType = StringUtil.getEnum(type, EntityType.class).orElse(null);
if (entityType == null) return;
e.getItems().forEach(item -> {
ItemStack drop = item.getItemStack();
if (drop.getType() == Material.PLAYER_HEAD) {
ItemUtil.setSkullTexture(drop, texture);
ItemUtil.mapMeta(drop, meta -> {
String name = this.headName.replace(Placeholders.GENERIC_TYPE, LangManager.getEntityType(entityType));
meta.setDisplayName(name);
PDCUtil.set(meta, this.skullKey, type);
});
}
item.setItemStack(drop);
});
});
}
}

View File

@ -1,5 +1,6 @@
package su.nightexpress.excellentenchants.enchantment.impl.weapon;
import org.bukkit.Color;
import org.bukkit.Particle;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.LivingEntity;
@ -8,7 +9,7 @@ import org.bukkit.inventory.ItemStack;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.jetbrains.annotations.NotNull;
import su.nexmedia.engine.utils.EffectUtil;
import su.nexmedia.engine.api.particle.SimpleParticle;
import su.nexmedia.engine.utils.random.Rnd;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.api.enchantment.meta.Chanced;
@ -54,7 +55,9 @@ public class EnchantSurprise extends PotionEnchant implements Chanced, CombatEnc
if (!victim.addPotionEffect(effect)) return false;
if (this.hasVisualEffects()) {
EffectUtil.playEffect(victim.getEyeLocation(), Particle.SPELL_WITCH, "", 0.25, 0.25, 0.25, 0.1f, 30);
Color color = Color.fromRGB(Rnd.nextInt(256), Rnd.nextInt(256), Rnd.nextInt(256));
Particle.DustOptions dustOptions = new Particle.DustOptions(color, 2f);
SimpleParticle.of(Particle.REDSTONE, dustOptions).play(victim.getEyeLocation(), 0.25, 0.1, 25);
}
return true;
}

View File

@ -188,21 +188,24 @@ public class EnchantGenericListener extends AbstractListener<ExcellentEnchants>
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onEnchantPopulateSpawn(CreatureSpawnEvent e) {
if (Config.getObtainSettings(ObtainType.MOB_SPAWNING).isEmpty()) return;
//if (Config.getObtainSettings(ObtainType.MOB_SPAWNING).isEmpty()) return;
LivingEntity entity = e.getEntity();
if (Hooks.isMythicMob(entity)) return;
EntityEquipment equipment = entity.getEquipment();
if (equipment == null) return;
this.plugin.runTaskLater(task -> {
EntityEquipment equipment = entity.getEquipment();
if (equipment == null) return;
for (EquipmentSlot slot : EquipmentSlot.values()) {
ItemStack item = equipment.getItem(slot);
if (EnchantManager.isEnchantable(item)) {
EnchantManager.populateEnchantments(item, ObtainType.MOB_SPAWNING);
EnchantManager.getExcellentEnchantments(item).keySet().forEach(enchant -> EnchantManager.restoreEnchantmentCharges(item, enchant));
equipment.setItem(slot, item);
boolean isMythic = Hooks.isMythicMob(entity);
boolean doPopulation = Config.getObtainSettings(ObtainType.MOB_SPAWNING).isPresent() && !isMythic;
for (EquipmentSlot slot : EquipmentSlot.values()) {
ItemStack item = equipment.getItem(slot);
if (EnchantManager.isEnchantable(item)) {
if (doPopulation) EnchantManager.populateEnchantments(item, ObtainType.MOB_SPAWNING);
EnchantManager.getExcellentEnchantments(item).keySet().forEach(enchant -> EnchantManager.restoreEnchantmentCharges(item, enchant));
equipment.setItem(slot, item);
}
}
}
}, 40L);
}
}

View File

@ -0,0 +1,85 @@
package su.nightexpress.excellentenchants.hook.impl;
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Player;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import su.nexmedia.engine.utils.StringUtil;
import su.nightexpress.excellentenchants.ExcellentEnchantsAPI;
import su.nightexpress.excellentenchants.api.enchantment.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.EnchantRegister;
public class PlaceholderHook {
private static EnchantsExpansion expansion;
public static void setup() {
if (expansion == null) {
expansion = new EnchantsExpansion();
expansion.register();
}
}
public static void shutdown() {
if (expansion != null) {
expansion.unregister();
expansion = null;
}
}
static class EnchantsExpansion extends PlaceholderExpansion {
@Override
@NotNull
public String getIdentifier() {
return "excellentenchants";
}
@Override
@NotNull
public String getAuthor() {
return ExcellentEnchantsAPI.PLUGIN.getDescription().getAuthors().get(0);
}
@Override
@NotNull
public String getVersion() {
return ExcellentEnchantsAPI.PLUGIN.getDescription().getVersion();
}
@Override
@Nullable
public String onPlaceholderRequest(Player player, @NotNull String params) {
if (params.startsWith("charges_remaining_")) {
String[] chargesSplit = params.substring("charges_remaining_".length()).split(":");
if (chargesSplit.length < 2) return null;
EquipmentSlot slot = StringUtil.getEnum(chargesSplit[0], EquipmentSlot.class).orElse(null);
if (slot == null) return null;
ItemStack item = player.getInventory().getItem(slot);
if (item == null || item.getType().isAir()) return "-";
ExcellentEnchant enchant = EnchantRegister.get(NamespacedKey.minecraft(chargesSplit[1].toLowerCase()));
if (enchant == null) return null;
return String.valueOf(enchant.getCharges(item));
}
if (params.startsWith("charges_maximum_")) {
String[] chargesSplit = params.substring("charges_maximum_".length()).split(":");
if (chargesSplit.length < 2) return null;
ExcellentEnchant enchant = EnchantRegister.get(NamespacedKey.minecraft(chargesSplit[0].toLowerCase()));
if (enchant == null) return null;
int level = StringUtil.getInteger(chargesSplit[1], 1);
return String.valueOf(enchant.getChargesMax(level));
}
return super.onPlaceholderRequest(player, params);
}
}
}

View File

@ -1,18 +1,37 @@
# Przetłumaczone przez nitolar play
# Tłumaczenie utworzono: 06.11.2020
Command:
List:
Desc: Lista wszystkich customowych enchantów.
Enchant:
Usage: <zaklęcie> <poziom>
Desc: Zaklnij przedmiot w swojej ręce.
Done: '&aSukcesywnie Zaklnięto!'
Usage: <enchant> <poziomXP>
Desc: Zaklina przedmiot trzymany w łapce.
Done: '&aPomyślnie zaklęto!'
Book:
Usage: <gracz> <zaklęcie> <poziom>
Desc: Dodaje niestandardowo zaklniętą książke.
Done: Dodano zaklniętą ksiązke z zaklęciem &6%enchant%&7 do gracza &6%player%&7.
Usage: <gracz> <enchant> <poziomXP>
Desc: Daje książkę z customowym enchantem.
Done: Przyznano zaklętą książkę &6%enchant%&7 dla &6%player_display_name%&7.
TierBook:
Usage: <gracz> <żadkość> <poziom>
Desc: Dodaje zaklniętą książke.
Error: '&cZły poziom żadkości!'
Done: Dodano zaklniętą ksiązke z zaklęciem &6%enchant%&7 do gracza &6%player%&7.
Usage: <gracz> <poziom> <poziomXP>
Desc: Daje książkę z enchantem.
Error: '&cZły poziom!'
Done: Przyznano zaklętą książkę &6%tier_name%&7 dla &6%player_display_name%&7.
Error:
NoEnchant: '&cNie ma takiego zaklęcia.'
NoEnchant: '&cNiepoprawny enchant.'
FitItemType:
HELMET: Hełm
CHESTPLATE: Napierśnik
LEGGINGS: Spodnie
BOOTS: Buty
ELYTRA: Elytra
WEAPON: Broń
TOOL: Narzędzie
ARMOR: Zbroja
UNIVERSAL: Uniwersalny
SWORD: Miecz
TRIDENT: Trójząb
AXE: Siekiera
BOW: Łuk
CROSSBOW: Kusza
HOE: Motyka
PICKAXE: Kilof
SHOVEL: Łopata
FISHING_ROD: Wędka

View File

@ -4,41 +4,6 @@ name: ExcellentEnchants
author: NightExpress
desciption: Vanilla-like enchants for your server.
depend: [ NexEngine ]
softdepend: [ ProtocolLib, NoCheatPlus ]
softdepend: [ ProtocolLib, NoCheatPlus, PlaceholderAPI ]
api-version: 1.17
load: STARTUP
permissions:
excellentenchants.admin:
description: Grants access to all plugin functions.
default: op
children:
excellentenchants.user: true
excellentenchants.command: true
excellentenchants.user:
description: Grants access to basic player plugin functions.
default: true
excellentenchants.command:
description: Grants access to all the plugin commands.
default: op
children:
excellentenchants.command.book: true
excellentenchants.command.enchant: true
excellentenchants.command.list: true
excellentenchants.command.tierbook: true
excellentenchants.command.book:
description: Grants access to /eenchants book command.
default: op
excellentenchants.command.enchant:
description: Grants access to /eenchants enchant command.
default: op
excellentenchants.command.list:
description: Grants access to /eenchants list command.
default: true
excellentenchants.command.tierbook:
description: Grants access to /eenchants tierbook command.
default: op
load: STARTUP