commit ab5b19db4dff219a77f8a57a1c978090da79dd7d Author: nulli0n Date: Fri Jan 21 19:24:32 2022 +0500 3.1.0 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..62a5987 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +/Core/pom.xml.versionsBackup +/pom.xml.versionsBackup +/NMS/pom.xml.versionsBackup +/V1_16_R1/pom.xml.versionsBackup +/V1_17_R1/pom.xml.versionsBackup +/V1_18_R1/pom.xml.versionsBackup +/Core/target/ +/NMS/target/ +/V1_16_R1/target/ +/V1_17_R1/target/ +/V1_18_R1/target/ +/.idea/ +/target/ diff --git a/Core/pom.xml b/Core/pom.xml new file mode 100644 index 0000000..0416183 --- /dev/null +++ b/Core/pom.xml @@ -0,0 +1,92 @@ + + + + ExcellentEnchants + su.nightexpress.excellentenchants + 3.1.0 + + 4.0.0 + + Core + + + 16 + 16 + + + + + md_5-releases + https://repo.md-5.net/content/repositories/releases/ + + + + + + org.spigotmc + spigot-api + 1.18.1-R0.1-SNAPSHOT + + + su.nightexpress.excellentenchants + NMS + 3.1.0 + + + su.nightexpress.excellentenchants + V1_16_R1 + 3.1.0 + + + su.nightexpress.excellentenchants + V1_17_R1 + 3.1.0 + + + su.nightexpress.excellentenchants + V1_18_R1 + 3.1.0 + + + fr.neatmonster + nocheatplus + 3.16.1-SNAPSHOT + provided + + + + + + + src/main/resources + true + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.4 + + + package + + shade + + + ${project.parent.basedir}\target\${project.parent.name}-${project.version}.jar + + + su.nightexpress.excellentenchants* + + + + + + + + + + \ No newline at end of file diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/ExcellentEnchants.java b/Core/src/main/java/su/nightexpress/excellentenchants/ExcellentEnchants.java new file mode 100644 index 0000000..a62b0c8 --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/ExcellentEnchants.java @@ -0,0 +1,122 @@ +package su.nightexpress.excellentenchants; + +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.NexPlugin; +import su.nexmedia.engine.Version; +import su.nexmedia.engine.api.command.GeneralCommand; +import su.nexmedia.engine.utils.Reflex; +import su.nightexpress.excellentenchants.command.BookCommand; +import su.nightexpress.excellentenchants.command.EnchantCommand; +import su.nightexpress.excellentenchants.command.ListCommand; +import su.nightexpress.excellentenchants.command.TierbookCommand; +import su.nightexpress.excellentenchants.config.Config; +import su.nightexpress.excellentenchants.config.Lang; +import su.nightexpress.excellentenchants.manager.EnchantManager; +import su.nightexpress.excellentenchants.nms.EnchantNMS; + +public class ExcellentEnchants extends NexPlugin { + + private static ExcellentEnchants instance; + + public static boolean isLoaded = false; + + private Config config; + private Lang lang; + + private EnchantNMS enchantNMS; + private EnchantManager enchantManager; + + public ExcellentEnchants() { + instance = this; + } + + public static ExcellentEnchants getInstance() { + return instance; + } + + @Override + public void enable() { + if (!this.setNMS()) { + this.error("Could not setup internal NMS handler!"); + this.getPluginManager().disablePlugin(this); + return; + } + + this.enchantManager = new EnchantManager(this); + this.enchantManager.setup(); + } + + @Override + public void disable() { + if (this.enchantManager != null) { + this.enchantManager.shutdown(); + this.enchantManager = null; + } + } + + @Override + public boolean useNewConfigFields() { + return true; + } + + private boolean setNMS() { + Version current = Version.CURRENT; + if (current == null) return false; + + String pack = EnchantNMS.class.getPackage().getName(); + Class clazz = Reflex.getClass(pack, current.name()); + if (clazz == null) return false; + + try { + this.enchantNMS = (EnchantNMS) clazz.getConstructor().newInstance(); + } + catch (Exception e) { + e.printStackTrace(); + } + return this.enchantNMS != null; + } + + @Override + public void setConfig() { + this.config = new Config(this); + this.config.setup(); + + this.lang = new Lang(this); + this.lang.setup(); + } + + @Override + public void registerCommands(@NotNull GeneralCommand mainCommand) { + mainCommand.addChildren(new BookCommand(this)); + mainCommand.addChildren(new EnchantCommand(this)); + mainCommand.addChildren(new ListCommand(this)); + mainCommand.addChildren(new TierbookCommand(this)); + } + + @Override + public void registerHooks() { + + } + + @Override + @NotNull + public Config cfg() { + return this.config; + } + + @Override + @NotNull + public Lang lang() { + return this.lang; + } + + @NotNull + public EnchantManager getEnchantManager() { + return this.enchantManager; + } + + @NotNull + public EnchantNMS getEnchantNMS() { + return enchantNMS; + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/Perms.java b/Core/src/main/java/su/nightexpress/excellentenchants/Perms.java new file mode 100644 index 0000000..7d190b2 --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/Perms.java @@ -0,0 +1,15 @@ +package su.nightexpress.excellentenchants; + +public class Perms { + + public static final String PREFIX = "excellentenchants."; + + public static final String ADMIN = PREFIX + "admin"; + public static final String USER = PREFIX + "user"; + + public static final String COMMAND_BOOK = PREFIX + "command.book"; + public static final String COMMAND_ENCHANT = PREFIX + "command.enchant"; + public static final String COMMAND_LIST = PREFIX + "command.list"; + public static final String COMMAND_TIERBOOK = PREFIX + "command.tierbook"; + +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/api/enchantment/EnchantPriority.java b/Core/src/main/java/su/nightexpress/excellentenchants/api/enchantment/EnchantPriority.java new file mode 100644 index 0000000..2ffa768 --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/api/enchantment/EnchantPriority.java @@ -0,0 +1,6 @@ +package su.nightexpress.excellentenchants.api.enchantment; + +public enum EnchantPriority { + + LOWEST, LOW, MEDIUM, HIGH, HIGHEST +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/api/enchantment/ExcellentEnchant.java b/Core/src/main/java/su/nightexpress/excellentenchants/api/enchantment/ExcellentEnchant.java new file mode 100644 index 0000000..31729ee --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/api/enchantment/ExcellentEnchant.java @@ -0,0 +1,308 @@ +package su.nightexpress.excellentenchants.api.enchantment; + +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import su.nexmedia.engine.api.config.JYML; +import su.nexmedia.engine.api.manager.IListener; +import su.nexmedia.engine.manager.leveling.Scaler; +import su.nexmedia.engine.utils.*; +import su.nexmedia.engine.utils.random.Rnd; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.config.Config; +import su.nightexpress.excellentenchants.manager.EnchantManager; +import su.nightexpress.excellentenchants.manager.object.EnchantScaler; +import su.nightexpress.excellentenchants.manager.object.EnchantTier; +import su.nightexpress.excellentenchants.manager.type.FitItemType; +import su.nightexpress.excellentenchants.manager.type.ObtainType; + +import java.util.*; +import java.util.function.UnaryOperator; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public abstract class ExcellentEnchant extends Enchantment implements IListener { + + public static final String PLACEHOLDER_NAME = "%enchantment_name%"; + public static final String PLACEHLDER_NAME_FORMATTED = "%enchantment_name_formatted%"; + public static final String PLACEHOLDER_DESCRIPTION = "%enchantment_description%"; + public static final String PLACEHOLDER_LEVEL = "%enchantment_level%"; + public static final String PLACEHOLDER_LEVEL_MIN = "%enchantment_level_min%"; + public static final String PLACEHOLDER_LEVEL_MAX = "%enchantment_level_max%"; + public static final String PLACEHOLDER_CONFLICTS = "%enchantment_conflicts%"; + public static final String PLACEHOLDER_TARGET = "%enchantment_target%"; + public static final String PLACEHOLDER_TIER = "%enchantment_tier%"; + public static final String PLACEHOLDER_FIT_ITEM_TYPES = "%enchantment_fit_item_types%"; + public static final String PLACEHOLDER_OBTAIN_CHANCE_ENCHANTING = "%enchantment_obtain_chance_enchanting%"; + public static final String PLACEHOLDER_OBTAIN_CHANCE_VILLAGER = "%enchantment_obtain_chance_villager%"; + public static final String PLACEHOLDER_OBTAIN_CHANCE_LOOT_GENERATION = "%enchantment_obtain_chance_loot_generation%"; + public static final String PLACEHOLDER_OBTAIN_CHANCE_FISHING = "%enchantment_obtain_chance_fishing%"; + public static final String PLACEHOLDER_OBTAIN_CHANCE_MOB_SPAWNING = "%enchantment_obtain_chance_mob_spawning%"; + public static final String PLACEHOLDER_COST_ITEM = "%enchantment_cost_item%"; + + protected final ExcellentEnchants plugin; + protected final JYML cfg; + protected final String id; + protected final EnchantPriority priority; + + protected String displayName; + protected EnchantTier tier; + protected List description; + + private final Set conflicts; + protected boolean isTreasure; + protected int levelMin; + protected int levelMax; + protected Scaler levelByEnchantCost; + protected Scaler anvilMergeCost; + protected Map obtainChance; + protected ItemStack costItem; + protected boolean costEnabled; + + public ExcellentEnchant(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg, @NotNull EnchantPriority priority) { + super(NamespacedKey.minecraft(cfg.getFile().getName().replace(".yml", "").toLowerCase())); + this.plugin = plugin; + this.id = this.getKey().getKey(); + this.cfg = cfg; + this.updateConfig(); + this.cfg.saveChanges(); + this.priority = priority; + + this.displayName = StringUtil.color(cfg.getString("Name", this.getId())); + this.tier = EnchantManager.getTierById(cfg.getString("Tier", Constants.DEFAULT)); + if (this.tier == null) { + throw new IllegalStateException("Invalid tier provided for the '" + id + "' enchantment!"); + } + this.tier.getEnchants().add(this); + this.description = StringUtil.color(cfg.getStringList("Description")); + + this.conflicts = new HashSet<>(); + this.isTreasure = cfg.getBoolean("Is_Treasure"); + this.levelMin = cfg.getInt("Level.Min"); + this.levelMax = cfg.getInt("Level.Max"); + this.levelByEnchantCost = new EnchantScaler(this, ObtainType.ENCHANTING.getPathName() + ".Level_By_Exp_Cost"); + this.anvilMergeCost = new EnchantScaler(this, "Anvil.Merge_Cost"); + + this.obtainChance = new HashMap<>(); + for (ObtainType obtainType : ObtainType.values()) { + double obtainChance = cfg.getDouble(obtainType.getPathName() + ".Chance"); + this.obtainChance.put(obtainType, obtainChance); + } + + this.costEnabled = cfg.getBoolean("Settings.Cost.Enabled"); + this.costItem = cfg.getItem("Settings.Cost.Item"); + } + + protected void updateConfig() { + cfg.addMissing("Is_Treasure", false); + cfg.addMissing("Settings.Cost.Enabled", false); + cfg.addMissing("Settings.Cost.Item.Material", Material.AIR.name()); + cfg.addMissing("Settings.Cost.Item.Amount", 1); + + if (cfg.contains("Enchantment_Table")) { + String path = ObtainType.ENCHANTING.getPathName() + "."; + cfg.addMissing(path + "Chance", cfg.getDouble("Enchantment_Table.Chance")); + cfg.addMissing(path + "Level_By_Exp_Cost", cfg.getString("Enchantment_Table.Level_By_Exp_Cost", "30")); + cfg.remove("Enchantment_Table"); + } + + for (ObtainType obtainType : ObtainType.values()) { + cfg.addMissing(obtainType.getPathName() + ".Chance", 25D); + } + } + + @NotNull + public UnaryOperator replacePlaceholders(int level) { + String conflicts = this.getConflicts().isEmpty() ? plugin.lang().Other_None.getMsg() : this.getConflicts().stream().filter(Objects::nonNull).map(en -> plugin.lang().getEnchantment(en)).collect(Collectors.joining("\n")); + + return str -> str + .replace(PLACEHOLDER_NAME, this.getDisplayName()) + .replace(PLACEHLDER_NAME_FORMATTED, this.getNameFormatted(level)) + .replace(PLACEHOLDER_LEVEL, NumberUtil.toRoman(level)) + .replace(PLACEHOLDER_LEVEL_MIN, String.valueOf(this.getStartLevel())) + .replace(PLACEHOLDER_LEVEL_MAX, String.valueOf(this.getMaxLevel())) + .replace(PLACEHOLDER_TARGET, plugin.lang().getEnum(this.getItemTarget())) + .replace(PLACEHOLDER_TIER, this.getTier().getName()) + .replace(PLACEHOLDER_CONFLICTS, conflicts) + .replace(PLACEHOLDER_FIT_ITEM_TYPES, String.join(", ", Stream.of(this.getFitItemTypes()).map(type -> plugin.lang().getEnum(type)).toList())) + .replace(PLACEHOLDER_OBTAIN_CHANCE_ENCHANTING, NumberUtil.format(this.getObtainChance(ObtainType.ENCHANTING))) + .replace(PLACEHOLDER_OBTAIN_CHANCE_VILLAGER, NumberUtil.format(this.getObtainChance(ObtainType.VILLAGER))) + .replace(PLACEHOLDER_OBTAIN_CHANCE_LOOT_GENERATION, NumberUtil.format(this.getObtainChance(ObtainType.LOOT_GENERATION))) + .replace(PLACEHOLDER_OBTAIN_CHANCE_FISHING, NumberUtil.format(this.getObtainChance(ObtainType.FISHING))) + .replace(PLACEHOLDER_OBTAIN_CHANCE_MOB_SPAWNING, NumberUtil.format(this.getObtainChance(ObtainType.MOB_SPAWNING))) + .replace(PLACEHOLDER_COST_ITEM, this.hasCostItem() ? ItemUtil.getItemName(this.costItem) : plugin.lang().Other_None.getMsg()) + ; + } + + @NotNull + public UnaryOperator formatString(int level) { + return str -> this.replacePlaceholders(level).apply(str + .replace(PLACEHOLDER_DESCRIPTION, String.join("\n", Config.formatDescription(this.getDescription()))) + ); + } + + @NotNull + public FitItemType[] getFitItemTypes() { + FitItemType itemType = FitItemType.getByEnchantmentTarget(this.getItemTarget()); + return itemType == null ? new FitItemType[0] : new FitItemType[]{itemType}; + } + + protected void addConflicts() { + + } + + protected void addConflict(@NotNull Enchantment enchantment) { + this.conflicts.add(enchantment); + } + + public boolean hasCostItem() { + return this.costEnabled && !this.costItem.getType().isAir(); + } + + public boolean takeCostItem(@NotNull LivingEntity livingEntity) { + if (!this.hasCostItem()) return true; + if (!(livingEntity instanceof Player player)) return true; + + if (PlayerUtil.countItem(player, this.costItem) < this.costItem.getAmount()) return false; + return PlayerUtil.takeItem(player, this.costItem, this.costItem.getAmount()); + } + + public boolean isEnchantmentAvailable(@NotNull LivingEntity entity) { + return !Config.isEnchantmentDisabled(this, entity.getWorld().getName()); + } + + @Override + public void registerListeners() { + this.addConflicts(); + this.plugin.getPluginManager().registerEvents(this, plugin); + } + + @NotNull + public JYML getConfig() { + return this.cfg; + } + + @NotNull + public String getId() { + return this.id; + } + + @NotNull + public EnchantPriority getPriority() { + return priority; + } + + @NotNull + @Override + public String getName() { + return getId().toUpperCase(); + } + + @NotNull + public String getDisplayName() { + return this.displayName; + } + + @NotNull + public String getNameFormatted(int level) { + return this.getTier().getColor() + this.getDisplayName() + " " + NumberUtil.toRoman(level); + } + + @NotNull + public List getDescription() { + return this.description; + } + + @NotNull + public List getDescription(int level) { + List description = new ArrayList<>(this.description); + description.replaceAll(this.replacePlaceholders(level)); + return description; + } + + @NotNull + public Set getConflicts() { + return conflicts; + } + + @NotNull + public EnchantTier getTier() { + return this.tier; + } + + @Override + public int getMaxLevel() { + return this.levelMax; + } + + @Override + public int getStartLevel() { + return this.levelMin; + } + + public int getLevelByEnchantCost(int expLevel) { + Optional> opt = this.levelByEnchantCost.getValues().entrySet().stream().filter(en -> expLevel >= en.getValue().intValue()).max(Comparator.comparingInt(Map.Entry::getKey)); + return opt.isPresent() ? opt.get().getKey() : Rnd.get(this.getStartLevel(), this.getMaxLevel()); + } + + public double getObtainChance(@NotNull ObtainType obtainType) { + return this.obtainChance.getOrDefault(obtainType, 0D); + } + + public int getAnvilMergeCost(int level) { + return (int) this.anvilMergeCost.getValue(level); + } + + @Override + public final boolean conflictsWith(@NotNull Enchantment enchantment) { + return this.conflicts.contains(enchantment); + } + + @Override + public final boolean canEnchantItem(@Nullable ItemStack item) { + if (item == null || item.getType().isAir()) return false; + if (EnchantManager.getItemEnchants(item).keySet().stream().anyMatch(e -> e.conflictsWith(this) || this.conflictsWith(e))) return false; + if (!item.containsEnchantment(this) && EnchantManager.getItemCustomEnchantsAmount(item) >= Config.ENCHANTMENTS_ITEM_CUSTOM_MAX) { + return false; + } + if (item.getType() == Material.BOOK || item.getType() == Material.ENCHANTED_BOOK) { + return true; + } + return Stream.of(this.getFitItemTypes()).anyMatch(fitItemType -> fitItemType.isIncluded(item)); + } + + /*protected boolean isFitItemType(@NotNull ItemStack item) { + EnchantmentTarget target = this.getItemTarget(); + return switch (target) { + case ARMOR -> ItemUtil.isArmor(item); + case ARMOR_FEET -> ItemUtil.isBoots(item); + case ARMOR_LEGS -> ItemUtil.isLeggings(item); + case ARMOR_TORSO -> ItemUtil.isChestplate(item) || (Config.ENCHANTMENTS_ITEM_ELYTRA_AS_CHESTPLATE && item.getType() == Material.ELYTRA); + case ARMOR_HEAD -> ItemUtil.isHelmet(item); + case WEAPON -> ItemUtil.isSword(item) || (Config.ENCHANTMENTS_ITEM_AXES_AS_SWORDS && ItemUtil.isAxe(item)); + case TOOL -> ItemUtil.isTool(item); + case BOW -> item.getType() == Material.BOW || (Config.ENCHANTMENTS_ITEM_CROSSBOWS_AS_BOWS && ItemUtil.isBow(item)); + case FISHING_ROD -> item.getType() == Material.FISHING_ROD; + case BREAKABLE -> true; + case WEARABLE -> EnchantManager.isEnchantable(item); + case TRIDENT -> ItemUtil.isTrident(item); + case CROSSBOW -> item.getType() == Material.CROSSBOW; + default -> false; + }; + }*/ + + @Override + public boolean isCursed() { + return false; + } + + @Override + public final boolean isTreasure() { + return this.isTreasure; + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/api/enchantment/IEnchantBowPotionTemplate.java b/Core/src/main/java/su/nightexpress/excellentenchants/api/enchantment/IEnchantBowPotionTemplate.java new file mode 100644 index 0000000..e321bba --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/api/enchantment/IEnchantBowPotionTemplate.java @@ -0,0 +1,71 @@ +package su.nightexpress.excellentenchants.api.enchantment; + +import org.bukkit.enchantments.EnchantmentTarget; +import org.bukkit.entity.Arrow; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Projectile; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.entity.EntityShootBowEvent; +import org.bukkit.event.entity.ProjectileHitEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.metadata.FixedMetadataValue; +import org.bukkit.potion.PotionEffectType; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.type.BowEnchant; + +public abstract class IEnchantBowPotionTemplate extends IEnchantPotionTemplate implements BowEnchant { + + protected final String arrowMeta; + + public IEnchantBowPotionTemplate(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg, + @NotNull EnchantPriority priority, + @NotNull PotionEffectType type) { + super(plugin, cfg, priority, type); + this.arrowMeta = this.getId() + "_potion_arrow"; + } + + public boolean isThisArrow(@NotNull Projectile projectile) { + return projectile.hasMetadata(this.arrowMeta); + } + + public void setThisArrow(@NotNull Projectile projectile) { + projectile.setMetadata(this.arrowMeta, new FixedMetadataValue(plugin, true)); + } + + @Override + @NotNull + public EnchantmentTarget getItemTarget() { + return EnchantmentTarget.BOW; + } + + @Override + public boolean use(@NotNull ProjectileHitEvent e, @NotNull Projectile projectile, @NotNull ItemStack bow, int level) { + if (!this.isThisArrow(projectile)) return false; + + return true; + } + + @Override + public boolean use(@NotNull EntityShootBowEvent e, @NotNull LivingEntity shooter, @NotNull ItemStack bow, int level) { + if (!this.isEnchantmentAvailable(shooter)) return false; + if (!(e.getProjectile() instanceof Arrow arrow)) return false; + if (!this.checkTriggerChance(level)) return false; + if (!bow.containsEnchantment(ARROW_INFINITE) && !this.takeCostItem(shooter)) return false; + + this.setThisArrow(arrow); + arrow.addCustomEffect(this.getEffect(level), true); + return true; + } + + @Override + public boolean use(@NotNull EntityDamageByEntityEvent e, @NotNull LivingEntity damager, @NotNull LivingEntity victim, @NotNull ItemStack weapon, int level) { + if (!this.isEnchantmentAvailable(victim)) return false; + if (!(e.getDamager() instanceof Projectile projectile)) return false; + if (!this.isThisArrow(projectile)) return false; + + //this.addEffect(victim, level); + return true; + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/api/enchantment/IEnchantBowTemplate.java b/Core/src/main/java/su/nightexpress/excellentenchants/api/enchantment/IEnchantBowTemplate.java new file mode 100644 index 0000000..a27cc07 --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/api/enchantment/IEnchantBowTemplate.java @@ -0,0 +1,86 @@ +package su.nightexpress.excellentenchants.api.enchantment; + +import org.bukkit.enchantments.EnchantmentTarget; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Projectile; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.entity.EntityShootBowEvent; +import org.bukkit.event.entity.ProjectileHitEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.metadata.FixedMetadataValue; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.type.BowEnchant; +import su.nightexpress.excellentenchants.manager.tasks.ArrowTrailsTask; + +public abstract class IEnchantBowTemplate extends IEnchantChanceTemplate implements BowEnchant { + + protected final String arrowTrailName; + protected final String arrowTrailData; + protected final String arrowMeta; + + public IEnchantBowTemplate(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg, @NotNull EnchantPriority priority) { + super(plugin, cfg, priority); + this.arrowMeta = this.getId() + "_arrow"; + this.arrowTrailName = cfg.getString("Settings.Arrow.Trail.Name", ""); + this.arrowTrailData = cfg.getString("Settings.Arrow.Trail.Data", ""); + } + + @Override + protected void updateConfig() { + super.updateConfig(); + + if (cfg.contains("Settings.Arrow.Trail") && !cfg.isConfigurationSection("Settings.Arrow.Trail")) { + String trail = cfg.getString("Settings.Arrow.Trail", ""); + + cfg.set("Settings.Arrow.Trail", null); + cfg.addMissing("Settings.Arrow.Trail.Name", trail); + } + cfg.addMissing("Settings.Arrow.Trail.Data", ""); + } + + public boolean isThisArrow(@NotNull Projectile projectile) { + return projectile.hasMetadata(this.arrowMeta); + } + + public void setThisArrow(@NotNull Projectile projectile) { + projectile.setMetadata(this.arrowMeta, new FixedMetadataValue(plugin, true)); + } + + @Override + @NotNull + public EnchantmentTarget getItemTarget() { + return EnchantmentTarget.BOW; + } + + @Override + public boolean use(@NotNull ProjectileHitEvent e, @NotNull Projectile projectile, @NotNull ItemStack bow, int level) { + if (!this.isThisArrow(projectile)) return false; + + return true; + } + + @Override + public boolean use(@NotNull EntityShootBowEvent e, @NotNull LivingEntity shooter, @NotNull ItemStack bow, int level) { + if (!this.isEnchantmentAvailable(shooter)) return false; + if (!(e.getProjectile() instanceof Projectile arrow)) return false; + if (!this.checkTriggerChance(level)) return false; + if (!bow.containsEnchantment(ARROW_INFINITE) && !this.takeCostItem(shooter)) return false; + + this.setThisArrow(arrow); + if (!this.arrowTrailName.isEmpty()) { + ArrowTrailsTask.add(arrow, this.arrowTrailName, this.arrowTrailData); + } + return true; + } + + @Override + public boolean use(@NotNull EntityDamageByEntityEvent e, @NotNull LivingEntity damager, @NotNull LivingEntity victim, @NotNull ItemStack weapon, int level) { + if (!this.isEnchantmentAvailable(victim)) return false; + if (!(e.getDamager() instanceof Projectile projectile)) return false; + if (!this.isThisArrow(projectile)) return false; + + return true; + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/api/enchantment/IEnchantChanceTemplate.java b/Core/src/main/java/su/nightexpress/excellentenchants/api/enchantment/IEnchantChanceTemplate.java new file mode 100644 index 0000000..cff2561 --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/api/enchantment/IEnchantChanceTemplate.java @@ -0,0 +1,51 @@ +package su.nightexpress.excellentenchants.api.enchantment; + +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +import su.nexmedia.engine.manager.leveling.Scaler; +import su.nexmedia.engine.utils.NumberUtil; +import su.nexmedia.engine.utils.random.Rnd; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.manager.object.EnchantScaler; + +import java.util.function.UnaryOperator; + +public abstract class IEnchantChanceTemplate extends ExcellentEnchant { + + public static final String PLACEHOLDER_CHANCE = "%enchantment_trigger_chance%"; + + protected Scaler triggerChance; + + public IEnchantChanceTemplate(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg, @NotNull EnchantPriority priority) { + super(plugin, cfg, priority); + + this.triggerChance = new EnchantScaler(this, "Settings.Trigger_Chance"); + } + + @Override + protected void updateConfig() { + super.updateConfig(); + + if (cfg.contains("settings.enchant-trigger-chance")) { + String triggerChance = cfg.getString("settings.enchant-trigger-chance", "100").replace("%level%", PLACEHOLDER_LEVEL); + + cfg.set("Settings.Trigger_Chance", triggerChance); + cfg.set("settings.enchant-trigger-chance", null); + } + } + + @Override + public @NotNull UnaryOperator replacePlaceholders(int level) { + return str -> super.replacePlaceholders(level).apply(str + .replace(PLACEHOLDER_CHANCE, NumberUtil.format(this.getTriggerChance(level))) + ); + } + + public final double getTriggerChance(int level) { + return this.triggerChance.getValue(level); + } + + public final boolean checkTriggerChance(int level) { + return Rnd.get(true) <= this.getTriggerChance(level); + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/api/enchantment/IEnchantCombatPotionTemplate.java b/Core/src/main/java/su/nightexpress/excellentenchants/api/enchantment/IEnchantCombatPotionTemplate.java new file mode 100644 index 0000000..0320831 --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/api/enchantment/IEnchantCombatPotionTemplate.java @@ -0,0 +1,53 @@ +package su.nightexpress.excellentenchants.api.enchantment; + +import org.bukkit.enchantments.EnchantmentTarget; +import org.bukkit.entity.LivingEntity; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.potion.PotionEffectType; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +import su.nexmedia.engine.utils.EffectUtil; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.type.CombatEnchant; + +public abstract class IEnchantCombatPotionTemplate extends IEnchantPotionTemplate implements CombatEnchant { + + protected String particleName; + protected String particleData; + + public IEnchantCombatPotionTemplate(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg, + @NotNull EnchantPriority priority, + @NotNull PotionEffectType effectType) { + super(plugin, cfg, priority, effectType); + + this.particleName = cfg.getString("Settings.Particle.Name", ""); + this.particleData = cfg.getString("Settings.Particle.Data", ""); + } + + @Override + protected void updateConfig() { + super.updateConfig(); + + cfg.remove("Settings.Particle_Effect"); + cfg.addMissing("Settings.Particle.Name", ""); + cfg.addMissing("Settings.Particle.Data", ""); + } + + @Override + @NotNull + public final EnchantmentTarget getItemTarget() { + return EnchantmentTarget.WEAPON; + } + + @Override + public boolean use(@NotNull EntityDamageByEntityEvent e, @NotNull LivingEntity damager, @NotNull LivingEntity victim, @NotNull ItemStack weapon, int level) { + if (!this.isEnchantmentAvailable(damager)) return false; + if (!this.checkTriggerChance(level)) return false; + if (!this.takeCostItem(damager)) return false; + if (!this.addEffect(victim, level)) return false; + + EffectUtil.playEffect(victim.getEyeLocation(), this.particleName, this.particleData, 0.25f, 0.25f, 0.25f, 0.1f, 50); + return true; + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/api/enchantment/IEnchantPotionTemplate.java b/Core/src/main/java/su/nightexpress/excellentenchants/api/enchantment/IEnchantPotionTemplate.java new file mode 100644 index 0000000..b31bfab --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/api/enchantment/IEnchantPotionTemplate.java @@ -0,0 +1,73 @@ +package su.nightexpress.excellentenchants.api.enchantment; + +import org.bukkit.entity.LivingEntity; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +import su.nexmedia.engine.manager.leveling.Scaler; +import su.nexmedia.engine.utils.NumberUtil; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.type.PassiveEnchant; +import su.nightexpress.excellentenchants.manager.EnchantManager; +import su.nightexpress.excellentenchants.manager.object.EnchantScaler; + +import java.util.function.UnaryOperator; + +public abstract class IEnchantPotionTemplate extends IEnchantChanceTemplate { + + public static final String PLACEHOLDER_POTION_LEVEL = "%enchantment_potion_level%"; + public static final String PLACEHOLDER_POTION_DURATION = "%enchantment_potion_duration%"; + public static final String PLACEHOLDER_POTION_TYPE = "%enchantment_potion_type%"; + + protected PotionEffectType potionEffectType; + protected Scaler potionDuration; + protected Scaler potionLevel; + protected final boolean potionParticles; + + public IEnchantPotionTemplate(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg, + @NotNull EnchantPriority priority, + @NotNull PotionEffectType potionEffectType) { + super(plugin, cfg, priority); + this.potionEffectType = potionEffectType; + this.potionDuration = new EnchantScaler(this, "Settings.Potion_Effect.Duration"); + this.potionLevel = new EnchantScaler(this, "Settings.Potion_Effect.Level"); + this.potionParticles = !(this instanceof PassiveEnchant); + } + + @Override + @NotNull + public UnaryOperator replacePlaceholders(int level) { + return str -> super.replacePlaceholders(level).apply(str + .replace(PLACEHOLDER_POTION_LEVEL, NumberUtil.toRoman(this.getEffectLevel(level))) + .replace(PLACEHOLDER_POTION_DURATION, NumberUtil.format((double) this.getEffectDuration(level) / 20D)) + .replace(PLACEHOLDER_POTION_TYPE, plugin.lang().getPotionType(this.getEffectType())) + ); + } + + @NotNull + public final PotionEffectType getEffectType() { + return this.potionEffectType; + } + + public final int getEffectDuration(int level) { + return (int) (this.potionDuration.getValue(level) * 20); + } + + public final int getEffectLevel(int level) { + return (int) this.potionLevel.getValue(level); + } + + @NotNull + public PotionEffect getEffect(int level) { + int duration = this.getEffectDuration(level); + int amplifier = Math.max(0, this.getEffectLevel(level) - 1); + + return new PotionEffect(this.potionEffectType, duration, amplifier, false, this.potionParticles); + } + + public final boolean addEffect(@NotNull LivingEntity target, int level) { + EnchantManager.addPotionEffect(target, this.getEffect(level), true); + return true; + } +} \ No newline at end of file diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/api/enchantment/type/BlockBreakEnchant.java b/Core/src/main/java/su/nightexpress/excellentenchants/api/enchantment/type/BlockBreakEnchant.java new file mode 100644 index 0000000..51ecd54 --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/api/enchantment/type/BlockBreakEnchant.java @@ -0,0 +1,11 @@ +package su.nightexpress.excellentenchants.api.enchantment.type; + +import org.bukkit.entity.Player; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +public interface BlockBreakEnchant { + + boolean use(@NotNull BlockBreakEvent e, @NotNull Player player, @NotNull ItemStack item, int level); +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/api/enchantment/type/BowEnchant.java b/Core/src/main/java/su/nightexpress/excellentenchants/api/enchantment/type/BowEnchant.java new file mode 100644 index 0000000..3f41021 --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/api/enchantment/type/BowEnchant.java @@ -0,0 +1,15 @@ +package su.nightexpress.excellentenchants.api.enchantment.type; + +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Projectile; +import org.bukkit.event.entity.EntityShootBowEvent; +import org.bukkit.event.entity.ProjectileHitEvent; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +public interface BowEnchant extends CombatEnchant { + + boolean use(@NotNull EntityShootBowEvent e, @NotNull LivingEntity shooter, @NotNull ItemStack bow, int level); + + boolean use(@NotNull ProjectileHitEvent e, @NotNull Projectile projectile, @NotNull ItemStack bow, int level); +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/api/enchantment/type/CombatEnchant.java b/Core/src/main/java/su/nightexpress/excellentenchants/api/enchantment/type/CombatEnchant.java new file mode 100644 index 0000000..0a78a0d --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/api/enchantment/type/CombatEnchant.java @@ -0,0 +1,11 @@ +package su.nightexpress.excellentenchants.api.enchantment.type; + +import org.bukkit.entity.LivingEntity; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +public interface CombatEnchant { + + boolean use(@NotNull EntityDamageByEntityEvent e, @NotNull LivingEntity damager, @NotNull LivingEntity victim, @NotNull ItemStack weapon, int level); +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/api/enchantment/type/CustomDropEnchant.java b/Core/src/main/java/su/nightexpress/excellentenchants/api/enchantment/type/CustomDropEnchant.java new file mode 100644 index 0000000..1d0607e --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/api/enchantment/type/CustomDropEnchant.java @@ -0,0 +1,15 @@ +package su.nightexpress.excellentenchants.api.enchantment.type; + +import org.bukkit.block.Block; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public interface CustomDropEnchant { + + @NotNull List getCustomDrops(@NotNull Player player, @NotNull ItemStack item, @NotNull Block block, int level); + + boolean isEventMustHaveDrops(); +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/api/enchantment/type/DeathEnchant.java b/Core/src/main/java/su/nightexpress/excellentenchants/api/enchantment/type/DeathEnchant.java new file mode 100644 index 0000000..4c823a7 --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/api/enchantment/type/DeathEnchant.java @@ -0,0 +1,10 @@ +package su.nightexpress.excellentenchants.api.enchantment.type; + +import org.bukkit.entity.LivingEntity; +import org.bukkit.event.entity.EntityDeathEvent; +import org.jetbrains.annotations.NotNull; + +public interface DeathEnchant { + + boolean use(@NotNull EntityDeathEvent e, @NotNull LivingEntity dead, int level); +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/api/enchantment/type/InteractEnchant.java b/Core/src/main/java/su/nightexpress/excellentenchants/api/enchantment/type/InteractEnchant.java new file mode 100644 index 0000000..94b0235 --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/api/enchantment/type/InteractEnchant.java @@ -0,0 +1,11 @@ +package su.nightexpress.excellentenchants.api.enchantment.type; + +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +public interface InteractEnchant { + + boolean use(@NotNull PlayerInteractEvent e, @NotNull Player player, @NotNull ItemStack item, int level); +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/api/enchantment/type/MoveEnchant.java b/Core/src/main/java/su/nightexpress/excellentenchants/api/enchantment/type/MoveEnchant.java new file mode 100644 index 0000000..a993c3e --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/api/enchantment/type/MoveEnchant.java @@ -0,0 +1,10 @@ +package su.nightexpress.excellentenchants.api.enchantment.type; + +import org.bukkit.entity.LivingEntity; +import org.bukkit.event.player.PlayerMoveEvent; +import org.jetbrains.annotations.NotNull; + +public interface MoveEnchant { + + boolean use(@NotNull PlayerMoveEvent e, @NotNull LivingEntity entity, int level); +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/api/enchantment/type/PassiveEnchant.java b/Core/src/main/java/su/nightexpress/excellentenchants/api/enchantment/type/PassiveEnchant.java new file mode 100644 index 0000000..e460c06 --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/api/enchantment/type/PassiveEnchant.java @@ -0,0 +1,9 @@ +package su.nightexpress.excellentenchants.api.enchantment.type; + +import org.bukkit.entity.LivingEntity; +import org.jetbrains.annotations.NotNull; + +public interface PassiveEnchant { + + boolean use(@NotNull LivingEntity entity, int level); +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/command/BookCommand.java b/Core/src/main/java/su/nightexpress/excellentenchants/command/BookCommand.java new file mode 100644 index 0000000..160feb9 --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/command/BookCommand.java @@ -0,0 +1,98 @@ +package su.nightexpress.excellentenchants.command; + +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.command.CommandSender; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.EnchantmentStorageMeta; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.command.AbstractCommand; +import su.nexmedia.engine.utils.PlayerUtil; +import su.nexmedia.engine.utils.StringUtil; +import su.nexmedia.engine.utils.random.Rnd; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.Perms; +import su.nightexpress.excellentenchants.manager.EnchantManager; + +import java.util.Arrays; +import java.util.List; + +public class BookCommand extends AbstractCommand { + + public BookCommand(@NotNull ExcellentEnchants plugin) { + super(plugin, new String[]{"book"}, Perms.COMMAND_BOOK); + } + + @Override + @NotNull + public String getDescription() { + return plugin.lang().Command_Book_Desc.getMsg(); + } + + @Override + @NotNull + public String getUsage() { + return plugin.lang().Command_Book_Usage.getMsg(); + } + + @Override + public boolean isPlayerOnly() { + return false; + } + + @Override + @NotNull + public List getTab(@NotNull Player player, int i, @NotNull String[] args) { + if (i == 1) { + return PlayerUtil.getPlayerNames(); + } + if (i == 2) { + return Arrays.stream(Enchantment.values()).map(e -> e.getKey().getKey()).toList(); + } + if (i == 3) { + return Arrays.asList("-1", "1", "5", "10"); + } + return super.getTab(player, i, args); + } + + @Override + public void onExecute(@NotNull CommandSender sender, @NotNull String label, @NotNull String[] args) { + if (args.length != 4) { + this.printUsage(sender); + return; + } + + Player player = plugin.getServer().getPlayer(args[1]); + if (player == null) { + this.errorPlayer(sender); + return; + } + + Enchantment enchantment = Enchantment.getByKey(NamespacedKey.minecraft(args[2].toLowerCase())); + if (enchantment == null) { + plugin.lang().Error_NoEnchant.send(sender); + return; + } + + int level = StringUtil.getInteger(args[3], -1, true); + if (level < 1) { + level = Rnd.get(enchantment.getStartLevel(), enchantment.getMaxLevel()); + } + + ItemStack item = new ItemStack(Material.ENCHANTED_BOOK); + EnchantmentStorageMeta meta = (EnchantmentStorageMeta) item.getItemMeta(); + if (meta == null) return; + + meta.addStoredEnchant(enchantment, level, true); + item.setItemMeta(meta); + + EnchantManager.updateItemLoreEnchants(item); + PlayerUtil.addItem(player, item); + + plugin.lang().Command_Book_Done + .replace("%enchant%", plugin.lang().getEnchantment(enchantment)) + .replace("%player%", player.getName()).send(sender); + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/command/EnchantCommand.java b/Core/src/main/java/su/nightexpress/excellentenchants/command/EnchantCommand.java new file mode 100644 index 0000000..5f434f7 --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/command/EnchantCommand.java @@ -0,0 +1,105 @@ +package su.nightexpress.excellentenchants.command; + +import org.bukkit.NamespacedKey; +import org.bukkit.command.CommandSender; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.EnchantmentStorageMeta; +import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.command.AbstractCommand; +import su.nexmedia.engine.utils.StringUtil; +import su.nexmedia.engine.utils.random.Rnd; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.Perms; +import su.nightexpress.excellentenchants.manager.EnchantManager; + +import java.util.Arrays; +import java.util.List; + +public class EnchantCommand extends AbstractCommand { + + public EnchantCommand(@NotNull ExcellentEnchants plugin) { + super(plugin, new String[]{"enchant"}, Perms.COMMAND_ENCHANT); + } + + @Override + @NotNull + public String getDescription() { + return plugin.lang().Command_Enchant_Desc.getMsg(); + } + + @Override + @NotNull + public String getUsage() { + return plugin.lang().Command_Enchant_Usage.getMsg(); + } + + @Override + public boolean isPlayerOnly() { + return true; + } + + @Override + @NotNull + public List getTab(@NotNull Player player, int i, @NotNull String[] args) { + if (i == 1) { + return Arrays.stream(Enchantment.values()).map(e -> e.getKey().getKey()).toList(); + } + if (i == 2) { + return Arrays.asList("-1", "1", "5", "10"); + } + return super.getTab(player, i, args); + } + + @Override + public void onExecute(@NotNull CommandSender sender, @NotNull String label, @NotNull String[] args) { + if (args.length != 3) { + this.printUsage(sender); + return; + } + + Player player = (Player) sender; + ItemStack item = player.getInventory().getItemInMainHand(); + if (item.getType().isAir()) { + this.errorItem(sender); + return; + } + + Enchantment enchantment = Enchantment.getByKey(NamespacedKey.minecraft(args[1].toLowerCase())); + if (enchantment == null) { + plugin.lang().Error_NoEnchant.send(sender); + return; + } + + int level = StringUtil.getInteger(args[2], -1, true); + if (level < 0) { + level = Rnd.get(enchantment.getStartLevel(), enchantment.getMaxLevel()); + } + + ItemMeta meta = item.getItemMeta(); + if (meta == null) return; + + if (meta instanceof EnchantmentStorageMeta) { + if (level == 0) { + ((EnchantmentStorageMeta) meta).removeStoredEnchant(enchantment); + } + else { + ((EnchantmentStorageMeta) meta).addStoredEnchant(enchantment, level, true); + } + } + else { + if (level == 0) { + meta.removeEnchant(enchantment); + } + else { + meta.addEnchant(enchantment, level, true); + } + } + item.setItemMeta(meta); + EnchantManager.updateItemLoreEnchants(item); + + plugin.lang().Command_Enchant_Done.send(sender); + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/command/ListCommand.java b/Core/src/main/java/su/nightexpress/excellentenchants/command/ListCommand.java new file mode 100644 index 0000000..b36c606 --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/command/ListCommand.java @@ -0,0 +1,37 @@ +package su.nightexpress.excellentenchants.command; + +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.command.AbstractCommand; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.Perms; + +public class ListCommand extends AbstractCommand { + + public ListCommand(@NotNull ExcellentEnchants plugin) { + super(plugin, new String[]{"list"}, Perms.COMMAND_LIST); + } + + @Override + @NotNull + public String getUsage() { + return ""; + } + + @Override + @NotNull + public String getDescription() { + return plugin.lang().Command_List_Desc.getMsg(); + } + + @Override + public boolean isPlayerOnly() { + return true; + } + + @Override + protected void onExecute(@NotNull CommandSender sender, @NotNull String label, @NotNull String[] args) { + plugin.getEnchantManager().getEnchantsListGUI().open((Player) sender, 1); + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/command/TierbookCommand.java b/Core/src/main/java/su/nightexpress/excellentenchants/command/TierbookCommand.java new file mode 100644 index 0000000..a6b835a --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/command/TierbookCommand.java @@ -0,0 +1,97 @@ +package su.nightexpress.excellentenchants.command; + +import org.bukkit.Material; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.command.AbstractCommand; +import su.nexmedia.engine.utils.PlayerUtil; +import su.nexmedia.engine.utils.StringUtil; +import su.nexmedia.engine.utils.random.Rnd; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.Perms; +import su.nightexpress.excellentenchants.api.enchantment.ExcellentEnchant; +import su.nightexpress.excellentenchants.manager.EnchantManager; +import su.nightexpress.excellentenchants.manager.object.EnchantTier; + +import java.util.Arrays; +import java.util.List; + +public class TierbookCommand extends AbstractCommand { + + public TierbookCommand(@NotNull ExcellentEnchants plugin) { + super(plugin, new String[]{"tierbook"}, Perms.COMMAND_TIERBOOK); + } + + @Override + @NotNull + public String getDescription() { + return plugin.lang().Command_TierBook_Desc.getMsg(); + } + + @Override + @NotNull + public String getUsage() { + return plugin.lang().Command_TierBook_Usage.getMsg(); + } + + @Override + public boolean isPlayerOnly() { + return false; + } + + @Override + @NotNull + public List getTab(@NotNull Player player, int i, @NotNull String[] args) { + if (i == 1) { + return PlayerUtil.getPlayerNames(); + } + if (i == 2) { + return EnchantManager.getTierIds(); + } + if (i == 3) { + return Arrays.asList("-1", "1", "5", "10"); + } + return super.getTab(player, i, args); + } + + @Override + public void onExecute(@NotNull CommandSender sender, @NotNull String label, @NotNull String[] args) { + if (args.length != 4) { + this.printUsage(sender); + return; + } + + Player player = plugin.getServer().getPlayer(args[1]); + if (player == null) { + this.errorPlayer(sender); + return; + } + + EnchantTier tier = EnchantManager.getTierById(args[2].toLowerCase()); + if (tier == null) { + plugin.lang().Command_TierBook_Error.send(sender); + return; + } + + ExcellentEnchant enchant = Rnd.get(tier.getEnchants()); + if (enchant == null) { + plugin.lang().Error_NoEnchant.send(sender); + return; + } + + int level = StringUtil.getInteger(args[3], -1, true); + if (level < 1) { + level = Rnd.get(enchant.getStartLevel(), enchant.getMaxLevel()); + } + + ItemStack item = new ItemStack(Material.ENCHANTED_BOOK); + EnchantManager.addEnchant(item, enchant, level, true); + PlayerUtil.addItem(player, item); + + plugin.lang().Command_TierBook_Done + .replace("%tier%", tier.getName()) + .replace("%player%", player.getName()).send(sender); + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/config/Config.java b/Core/src/main/java/su/nightexpress/excellentenchants/config/Config.java new file mode 100644 index 0000000..d8ac4b2 --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/config/Config.java @@ -0,0 +1,169 @@ +package su.nightexpress.excellentenchants.config; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import su.nexmedia.engine.api.config.ConfigTemplate; +import su.nexmedia.engine.utils.Constants; +import su.nexmedia.engine.utils.StringUtil; +import su.nexmedia.engine.utils.random.Rnd; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.ExcellentEnchant; +import su.nightexpress.excellentenchants.manager.object.EnchantTier; +import su.nightexpress.excellentenchants.manager.type.ObtainType; + +import java.util.*; +import java.util.stream.Collectors; + +public class Config extends ConfigTemplate { + + public Config(@NotNull ExcellentEnchants plugin) { + super(plugin); + } + + public static long TASKS_ARROW_TRAIL_TICKS_INTERVAL; + public static long TASKS_PASSIVE_ENCHANTS_TICKS_INTERVAL; + + public static Set ENCHANTMENTS_DISABLED; + public static Map> ENCHANTMENTS_DISABLED_IN_WORLDS; + public static boolean ENCHANTMENTS_DESCRIPTION_ENABLED; + private static String ENCHANTMENTS_DESCRIPTION_FORMAT; + + public static int ENCHANTMENTS_ITEM_CUSTOM_MAX; + public static boolean ENCHANTMENTS_ITEM_AXES_AS_SWORDS; + public static boolean ENCHANTMENTS_ITEM_CROSSBOWS_AS_BOWS; + public static boolean ENCHANTMENTS_ITEM_ELYTRA_AS_CHESTPLATE; + public static boolean ENCHANTMENTS_ENTITY_PASSIVE_FOR_MOBS; + + private static Map OBTAIN_SETTINGS; + private static Map TIERS; + + @Override + public void load() { + String path = "General.Tasks."; + TASKS_ARROW_TRAIL_TICKS_INTERVAL = cfg.getLong(path + "Arrow_Trails.Ticks_Interval", 1); + TASKS_PASSIVE_ENCHANTS_TICKS_INTERVAL = cfg.getLong(path + "Passive_Enchants.Ticks_Interval", 100); + + path = "General.Enchantments."; + cfg.addMissing(path + "Disabled_In_Worlds.my_world", Collections.singletonList(Constants.MASK_ANY)); + cfg.addMissing(path + "Disabled_In_Worlds.other_world", Arrays.asList("enchant_name", "another_enchant")); + + ENCHANTMENTS_DISABLED = cfg.getStringSet(path + "Disabled").stream().map(String::toLowerCase).collect(Collectors.toSet()); + ENCHANTMENTS_DISABLED_IN_WORLDS = new HashMap<>(); + for (String worldName : cfg.getSection(path + "Disabled_In_Worlds")) { + ENCHANTMENTS_DISABLED_IN_WORLDS.put(worldName, cfg.getStringSet(path + "Disabled_In_Worlds." + worldName) + .stream().map(String::toLowerCase).collect(Collectors.toSet())); + } + + ENCHANTMENTS_DESCRIPTION_ENABLED = cfg.getBoolean(path + "Description.Enabled"); + ENCHANTMENTS_DESCRIPTION_FORMAT = StringUtil.color(cfg.getString(path + "Description.Format", "")); + + path = "General.Enchantments.Item."; + ENCHANTMENTS_ITEM_CUSTOM_MAX = cfg.getInt(path + "Max_Custom_Enchants", 3); + ENCHANTMENTS_ITEM_AXES_AS_SWORDS = cfg.getBoolean(path + "Axes_As_Swords"); + ENCHANTMENTS_ITEM_CROSSBOWS_AS_BOWS = cfg.getBoolean(path + "Crossbows_As_Bows"); + ENCHANTMENTS_ITEM_ELYTRA_AS_CHESTPLATE = cfg.getBoolean(path + "Elytra_As_Chestplate"); + + path = "General.Enchantments.Entity."; + ENCHANTMENTS_ENTITY_PASSIVE_FOR_MOBS = cfg.getBoolean(path + "Passive_Enchants_Applied_To_Mobs"); + + OBTAIN_SETTINGS = new HashMap<>(); + for (ObtainType obtainType : ObtainType.values()) { + String path2 = "General." + obtainType.getPathName() + "."; + + cfg.addMissing(path2 + "Enabled", true); + cfg.addMissing(path2 + "Enchantments.Total_Maximum", 4); + cfg.addMissing(path2 + "Enchantments.Custom_Generation_Chance", 50D); + cfg.addMissing(path2 + "Enchantments.Custom_Minimum", 0); + cfg.addMissing(path2 + "Enchantments.Custom_Maximum", 2); + + if (!cfg.getBoolean(path2 + "Enabled")) continue; + + int enchantsTotalMax = cfg.getInt(path2 + "Enchantments.Total_Maximum", 4); + double enchantsCustomGenerationChance = cfg.getDouble(path2 + "Enchantments.Custom_Generation_Chance", 50D); + int enchantsCustomMin = cfg.getInt(path2 + "Enchantments.Custom_Minimum", 0); + int enchantsCustomMax = cfg.getInt(path2 + "Enchantments.Custom_Maximum", 2); + + ObtainSettings settings = new ObtainSettings(enchantsTotalMax, enchantsCustomGenerationChance, enchantsCustomMin, enchantsCustomMax); + OBTAIN_SETTINGS.put(obtainType, settings); + } + + this.setupTiers(); + } + + private void setupTiers() { + // Reloading tiers will reset their lists with enchants = break the plugin mechanics + if (ExcellentEnchants.isLoaded) return; + + TIERS = new HashMap<>(); + + // No tiers defined, setup a default one. + // Every enchantment must have a tier. + if (cfg.getSection("Tiers").isEmpty()) { + this.plugin.info("No tiers defined! Creating a default one for you..."); + cfg.set("Tiers.default.Name", "&7Default"); + cfg.set("Tiers.default.Color", "&7"); + for (ObtainType obtainType : ObtainType.values()) { + cfg.set("Tiers.default.Obtain_Chance." + obtainType.name(), 100D); + } + } + + // Load existing tiers. + for (String sId : cfg.getSection("Tiers")) { + String path = "Tiers." + sId + "."; + cfg.addMissing(path + "Priority", 0); + + int priority = cfg.getInt(path + "Priority"); + String name = cfg.getString(path + "Name", sId); + String color = cfg.getString(path + "Color", "&f"); + Map chance = new HashMap<>(); + + for (ObtainType obtainType : ObtainType.values()) { + cfg.addMissing(path + "Obtain_Chance." + obtainType.name(), 50D); + + double chanceType = cfg.getDouble(path + "Obtain_Chance." + obtainType.name()); + chance.put(obtainType, chanceType); + } + + EnchantTier tier = new EnchantTier(sId, priority, name, color, chance); + TIERS.put(tier.getId(), tier); + } + + this.plugin.info("Tiers Loaded: " + TIERS.size()); + } + + public static boolean isEnchantmentDisabled(@NotNull ExcellentEnchant enchant, @NotNull String world) { + Set disabled = ENCHANTMENTS_DISABLED_IN_WORLDS.getOrDefault(world, Collections.emptySet()); + return disabled.contains(enchant.getKey().getKey()) || disabled.contains(Constants.MASK_ANY); + } + + @Nullable + public static EnchantTier getTierById(@NotNull String id) { + return TIERS.get(id.toLowerCase()); + } + + @NotNull + public static Collection getTiers() { + return TIERS.values(); + } + + @NotNull + public static List getTierIds() { + return new ArrayList<>(TIERS.keySet()); + } + + @Nullable + public static EnchantTier getTierByChance(@NotNull ObtainType obtainType) { + Map map = getTiers().stream().collect(Collectors.toMap(k -> k, v -> v.getChance(obtainType))); + return Rnd.get(map); + } + + @Nullable + public static ObtainSettings getObtainSettings(@NotNull ObtainType obtainType) { + return OBTAIN_SETTINGS.get(obtainType); + } + + @NotNull + public static List formatDescription(@NotNull List description) { + return new ArrayList<>(description.stream().map(line -> ENCHANTMENTS_DESCRIPTION_FORMAT.replace("%description%", line)).toList()); + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/config/Lang.java b/Core/src/main/java/su/nightexpress/excellentenchants/config/Lang.java new file mode 100644 index 0000000..7c9029e --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/config/Lang.java @@ -0,0 +1,40 @@ +package su.nightexpress.excellentenchants.config; + +import org.bukkit.enchantments.EnchantmentTarget; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.LangMessage; +import su.nexmedia.engine.core.config.CoreLang; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.manager.type.FitItemType; + +public class Lang extends CoreLang { + + public Lang(@NotNull ExcellentEnchants plugin) { + super(plugin); + } + + @Override + public void setup() { + super.setup(); + this.setupEnum(EnchantmentTarget.class); + this.setupEnum(FitItemType.class); + } + + public LangMessage Command_List_Desc = new LangMessage(this, "List of all custom enchantments."); + + public LangMessage Command_Enchant_Usage = new LangMessage(this, " "); + public LangMessage Command_Enchant_Desc = new LangMessage(this, "Enchants the item in your hand."); + public LangMessage Command_Enchant_Done = new LangMessage(this, "&aSuccessfully enchanted!"); + + public LangMessage Command_Book_Usage = new LangMessage(this, " "); + public LangMessage Command_Book_Desc = new LangMessage(this, "Gives custom enchanted book."); + public LangMessage Command_Book_Done = new LangMessage(this, "Given &6%enchant%&7 enchanted book to &6%player%&7."); + + public LangMessage Command_TierBook_Usage = new LangMessage(this, " "); + public LangMessage Command_TierBook_Desc = new LangMessage(this, "Gives an enchanted book."); + public LangMessage Command_TierBook_Error = new LangMessage(this, "&cInvalid tier!"); + public LangMessage Command_TierBook_Done = new LangMessage(this, "Given &6%tier%&7 enchanted book to &6%player%&7."); + + public LangMessage Error_NoEnchant = new LangMessage(this, "&cNo such enchant."); + +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/config/ObtainSettings.java b/Core/src/main/java/su/nightexpress/excellentenchants/config/ObtainSettings.java new file mode 100644 index 0000000..468ccee --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/config/ObtainSettings.java @@ -0,0 +1,37 @@ +package su.nightexpress.excellentenchants.config; + +public class ObtainSettings { + + private final int enchantsTotalMax; + private final double enchantsCustomGenerationChance; + private final int enchantsCustomMin; + private final int enchantsCustomMax; + + public ObtainSettings(int enchantsTotalMax, double enchantsCustomGenerationChance, int enchantsCustomMin, int enchantsCustomMax) { + this.enchantsTotalMax = enchantsTotalMax; + this.enchantsCustomGenerationChance = enchantsCustomGenerationChance; + this.enchantsCustomMin = enchantsCustomMin; + this.enchantsCustomMax = enchantsCustomMax; + } + + public int getEnchantsTotalMax() { + return enchantsTotalMax; + } + + public double getEnchantsCustomGenerationChance() { + return enchantsCustomGenerationChance; + } + + public int getEnchantsCustomMin() { + return enchantsCustomMin; + } + + public int getEnchantsCustomMax() { + return enchantsCustomMax; + } + + @Override + public String toString() { + return "ObtainSettings{" + "enchantsTotalMax=" + enchantsTotalMax + ", enchantsCustomGenerationChance=" + enchantsCustomGenerationChance + ", enchantsCustomMin=" + enchantsCustomMin + ", enchantsCustomMax=" + enchantsCustomMax + '}'; + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/hook/HookId.java b/Core/src/main/java/su/nightexpress/excellentenchants/hook/HookId.java new file mode 100644 index 0000000..ebc3a0a --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/hook/HookId.java @@ -0,0 +1,6 @@ +package su.nightexpress.excellentenchants.hook; + +public class HookId { + + public static final String NCP = "NoCheatPlus"; +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/hook/HookNCP.java b/Core/src/main/java/su/nightexpress/excellentenchants/hook/HookNCP.java new file mode 100644 index 0000000..f830c75 --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/hook/HookNCP.java @@ -0,0 +1,23 @@ +package su.nightexpress.excellentenchants.hook; + +import fr.neatmonster.nocheatplus.NCPAPIProvider; +import fr.neatmonster.nocheatplus.checks.CheckType; +import fr.neatmonster.nocheatplus.hooks.ExemptionContext; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.hooks.Hooks; + +public class HookNCP { + + public static void exemptBlocks(@NotNull Player player) { + if (!Hooks.hasPlugin(HookId.NCP)) return; + + NCPAPIProvider.getNoCheatPlusAPI().getPlayerDataManager().getPlayerData(player).exempt(CheckType.BLOCKBREAK, ExemptionContext.ANONYMOUS_NESTED); + } + + public static void unexemptBlocks(@NotNull Player player) { + if (!Hooks.hasPlugin(HookId.NCP)) return; + + NCPAPIProvider.getNoCheatPlusAPI().getPlayerDataManager().getPlayerData(player).unexempt(CheckType.BLOCKBREAK, ExemptionContext.ANONYMOUS_NESTED); + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/EnchantManager.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/EnchantManager.java new file mode 100644 index 0000000..ee3f595 --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/EnchantManager.java @@ -0,0 +1,259 @@ +package su.nightexpress.excellentenchants.manager; + +import org.bukkit.Material; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.enchantments.EnchantmentTarget; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Projectile; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.EnchantmentStorageMeta; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.potion.PotionEffect; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import su.nexmedia.engine.api.manager.AbstractManager; +import su.nexmedia.engine.utils.ItemUtil; +import su.nexmedia.engine.utils.random.Rnd; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.ExcellentEnchant; +import su.nightexpress.excellentenchants.config.Config; +import su.nightexpress.excellentenchants.config.ObtainSettings; +import su.nightexpress.excellentenchants.manager.listeners.EnchantGenericListener; +import su.nightexpress.excellentenchants.manager.listeners.EnchantHandlerListener; +import su.nightexpress.excellentenchants.manager.object.EnchantListGUI; +import su.nightexpress.excellentenchants.manager.object.EnchantTier; +import su.nightexpress.excellentenchants.manager.tasks.ArrowTrailsTask; +import su.nightexpress.excellentenchants.manager.tasks.EnchantEffectPassiveTask; +import su.nightexpress.excellentenchants.manager.type.ObtainType; + +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class EnchantManager extends AbstractManager { + + private EnchantListGUI enchantListGUI; + private ArrowTrailsTask arrowTrailsTask; + private EnchantEffectPassiveTask enchantEffectPassiveTask; + + private static final Map PROJECTILE_WEAPON = new HashMap<>(); + + public EnchantManager(@NotNull ExcellentEnchants plugin) { + super(plugin); + } + + @Override + protected void onLoad() { + EnchantRegister.setup(); + + this.enchantListGUI = new EnchantListGUI(this.plugin); + this.addListener(new EnchantHandlerListener(this)); + this.addListener(new EnchantGenericListener(this)); + + this.arrowTrailsTask = new ArrowTrailsTask(this.plugin); + this.arrowTrailsTask.start(); + + this.enchantEffectPassiveTask = new EnchantEffectPassiveTask(this.plugin); + this.enchantEffectPassiveTask.start(); + } + + @Override + protected void onShutdown() { + if (this.enchantListGUI != null) { + this.enchantListGUI.clear(); + this.enchantListGUI = null; + } + if (this.arrowTrailsTask != null) { + this.arrowTrailsTask.stop(); + this.arrowTrailsTask = null; + } + if (this.enchantEffectPassiveTask != null) { + this.enchantEffectPassiveTask.stop(); + this.enchantEffectPassiveTask = null; + } + EnchantRegister.shutdown(); + } + + @NotNull + public EnchantListGUI getEnchantsListGUI() { + return enchantListGUI; + } + + public static void addPotionEffect(@NotNull LivingEntity entity, @NotNull PotionEffect effect, boolean compat) { + if (compat) { + PotionEffect has = entity.getPotionEffect(effect.getType()); + if (has != null && has.getAmplifier() > effect.getAmplifier()) { + return; + } + } + else { + entity.removePotionEffect(effect.getType()); + } + entity.addPotionEffect(effect); + } + + public static boolean isEnchantable(@NotNull ItemStack item) { + if (item.getType().isAir()) return false; + + return item.getType() == Material.ENCHANTED_BOOK || Stream.of(EnchantmentTarget.values()).anyMatch(target -> target.includes(item)) + /*|| ItemUtil.isWeapon(item) || ItemUtil.isArmor(item) || ItemUtil.isTool(item) || ItemUtil.isBow(item)*/; + } + + public static boolean populateEnchantments(@NotNull ItemStack item, @NotNull ObtainType obtainType) { + ObtainSettings settings = Config.getObtainSettings(obtainType); + if (settings == null) return false; + + if (Rnd.get(true) > settings.getEnchantsCustomGenerationChance()) return false; + + int enchHas = EnchantManager.getItemEnchantsAmount(item); + int enchMax = settings.getEnchantsTotalMax(); + int enchRoll = Rnd.get(settings.getEnchantsCustomMin(), settings.getEnchantsCustomMax()); + + for (int count = 0; (count < enchRoll && count + enchHas < enchMax); count++) { + EnchantTier tier = EnchantManager.getTierByChance(obtainType); + if (tier == null) continue; + + ExcellentEnchant enchant = tier.getEnchant(obtainType, item); + if (enchant == null) continue; + + int level = Rnd.get(enchant.getStartLevel(), enchant.getMaxLevel()); + EnchantManager.addEnchant(item, enchant, level, false); + } + EnchantManager.updateItemLoreEnchants(item); + return true; + } + + @Deprecated + public static boolean hasEnchant(@NotNull ItemStack item, @NotNull Enchantment enchantment) { + return item.getEnchantmentLevel(enchantment) != 0; + } + + @Deprecated + public static int getEnchantLevel(@NotNull ItemStack item, @NotNull Enchantment enchantment) { + ItemMeta meta = item.getItemMeta(); + return meta != null ? meta.getEnchantLevel(enchantment) : item.getEnchantmentLevel(enchantment); + } + + public static void updateItemLoreEnchants(@NotNull ItemStack item) { + EnchantRegister.ENCHANT_LIST.forEach(ench -> { + ItemUtil.delLore(item, ench.getId()); + ItemUtil.delLore(item, ench.getId() + "_info"); + }); + + // Filter custom enchants and define map order. + Map excellents = getItemCustomEnchants(item).entrySet().stream() + .sorted((e1,e2) -> e2.getKey().getTier().getPriority() - e1.getKey().getTier().getPriority()) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (has, add) -> add, LinkedHashMap::new)); + + excellents.forEach((excellent, level) -> { + ItemUtil.addLore(item, excellent.getId(), excellent.getNameFormatted(level), 0); + }); + + // Add enchantment description at the end of item lore. + if (Config.ENCHANTMENTS_DESCRIPTION_ENABLED) { + List list = new ArrayList<>(excellents.keySet()); + Collections.reverse(list); + + list.forEach(excellent -> { + List desc = excellent.getDescription(excellents.get(excellent)); + if (desc.isEmpty()) return; + + ItemUtil.addLore(item, excellent.getId() + "_info", Config.formatDescription(desc), -1); + }); + } + } + + public static boolean addEnchant(@NotNull ItemStack item, @NotNull ExcellentEnchant ench, int level, boolean force) { + if (!force && !ench.canEnchantItem(item)) return false; + + EnchantManager.removeEnchant(item, ench); + ItemUtil.addLore(item, ench.getId(), ench.getNameFormatted(level), 0); + + ItemMeta meta = item.getItemMeta(); + if (meta == null) return false; + + if (meta instanceof EnchantmentStorageMeta meta2) { + if (!meta2.addStoredEnchant(ench, level, true)) return false; + } + else { + if (!meta.addEnchant(ench, level, true)) return false; + } + item.setItemMeta(meta); + + return true; + } + + public static void removeEnchant(@NotNull ItemStack item, @NotNull ExcellentEnchant en) { + ItemUtil.delLore(item, en.getId()); + + ItemMeta meta = item.getItemMeta(); + if (meta instanceof EnchantmentStorageMeta meta2) { + meta2.removeStoredEnchant(en); + } + else { + meta.removeEnchant(en); + } + item.setItemMeta(meta); + } + + @NotNull + public static Map getItemCustomEnchants(@NotNull ItemStack item) { + return EnchantManager.getItemEnchants(item).entrySet().stream() + .filter(entry -> entry.getKey() instanceof ExcellentEnchant) + .map(entry -> new AbstractMap.SimpleEntry<>((ExcellentEnchant) entry.getKey(), entry.getValue())) + .sorted((e1,e2) -> e2.getKey().getPriority().ordinal() - e1.getKey().getPriority().ordinal()) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (old, nev) -> nev, LinkedHashMap::new)); + } + + @SuppressWarnings("unchecked") + @NotNull + public static Map getItemCustomEnchants(@NotNull ItemStack item, @NotNull Class clazz) { + return EnchantManager.getItemCustomEnchants(item).entrySet().stream() + .filter(entry -> clazz.isAssignableFrom(entry.getKey().getClass())) + .sorted((e1,e2) -> e2.getKey().getPriority().ordinal() - e1.getKey().getPriority().ordinal()) + .collect(Collectors.toMap(k -> (T) k.getKey(), Map.Entry::getValue, (old, nev) -> nev, LinkedHashMap::new)); + } + + public static int getItemCustomEnchantsAmount(@NotNull ItemStack item) { + return EnchantManager.getItemCustomEnchants(item).size(); + } + + @NotNull + public static Map getItemEnchants(@NotNull ItemStack item) { + ItemMeta meta = item.getItemMeta(); + return (meta instanceof EnchantmentStorageMeta meta2) ? meta2.getStoredEnchants() : meta.getEnchants(); + } + + public static int getItemEnchantsAmount(@NotNull ItemStack item) { + return EnchantManager.getItemEnchants(item).size(); + } + + @Nullable + public static EnchantTier getTierById(@NotNull String id) { + return Config.getTierById(id); + } + + @NotNull + public static Collection getTiers() { + return Config.getTiers(); + } + + @NotNull + public static List getTierIds() { + return Config.getTierIds(); + } + + @Nullable + public static EnchantTier getTierByChance(@NotNull ObtainType obtainType) { + return Config.getTierByChance(obtainType); + } + + public static void setArrowWeapon(@NotNull Projectile projectile, @NotNull ItemStack bow) { + PROJECTILE_WEAPON.put(projectile.getUniqueId(), bow); + } + + @Nullable + public static ItemStack getArrowWeapon(@NotNull Projectile projectile) { + return PROJECTILE_WEAPON.get(projectile.getUniqueId()); + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/EnchantRegister.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/EnchantRegister.java new file mode 100644 index 0000000..48f59d1 --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/EnchantRegister.java @@ -0,0 +1,221 @@ +package su.nightexpress.excellentenchants.manager; + +import org.bukkit.NamespacedKey; +import org.bukkit.enchantments.Enchantment; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import su.nexmedia.engine.api.config.JYML; +import su.nexmedia.engine.api.manager.ICleanable; +import su.nexmedia.engine.utils.Reflex; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.ExcellentEnchant; +import su.nightexpress.excellentenchants.config.Config; +import su.nightexpress.excellentenchants.manager.enchants.armor.*; +import su.nightexpress.excellentenchants.manager.enchants.bow.*; +import su.nightexpress.excellentenchants.manager.enchants.tool.*; +import su.nightexpress.excellentenchants.manager.enchants.weapon.*; + +import java.lang.reflect.Field; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class EnchantRegister { + + private static final ExcellentEnchants PLUGIN; + public static final Set ENCHANT_LIST; + + public static final EnchantBlastMining BLAST_MINING; + public static final EnchantCurseOfBreaking CURSE_OF_BREAKING; + public static final EnchantCurseOfMisfortune CURSE_OF_MISFORTUNE; + public static final EnchantDivineTouch DIVINE_TOUCH; + public static final EnchantHaste HASTE; + public static final EnchantLuckyMiner LUCKY_MINER; + public static final EnchantReplanter REPLANTER; + public static final EnchantSilkChest SILK_CHEST; + public static final EnchantSmelter SMELTER; + public static final EnchantTelekinesis TELEKINESIS; + public static final EnchantTreasures TREASURES; + public static final EnchantTunnel TUNNEL; + public static final EnchantVeinminer VEINMINER; + + public static final EnchantBaneOfNetherspawn BANE_OF_NETHERSPAWN; + public static final EnchantIceAspect ICE_ASPECT; + public static final EnchantInfernus INFERNUS; + public static final EnchantVenom VENOM; + public static final EnchantExhaust EXHAUST; + public static final EnchantWither WITHER; + public static final EnchantParalyze PARALYZE; + public static final EnchantExpHunter EXP_HUNTER; + public static final EnchantDecapitator DECAPITATOR; + public static final EnchantCutter CUTTER; + public static final EnchantConfusion CONFUSION; + public static final EnchantDoubleStrike DOUBLE_STRIKE; + public static final EnchantNimble NIMBLE; + public static final EnchantBlindness BLINDNESS; + public static final EnchantVampire VAMPIRE; + public static final EnchantCure CURE; + public static final EnchantRage RAGE; + public static final EnchantScavenger SCAVENGER; + public static final EnchantSurprise SURPRISE; + public static final EnchantThrifty THRIFTY; + public static final EnchantThunder THUNDER; + public static final EnchantVillageDefender VILLAGE_DEFENDER; + public static final EnchantRocket ROCKET; + + public static final EnchantFlameWalker FLAME_WALKER; + public static final EnchantHardened HARDENED; + public static final EnchantColdSteel COLD_STEEL; + public static final EnchantSelfDestruction SELF_DESTRUCTION; + public static final EnchantSaturation SATURATION; + public static final EnchantAquaman AQUAMAN; + public static final EnchantNightVision NIGHT_VISION; + public static final EnchantBunnyHop BUNNY_HOP; + public static final EnchantSonic SONIC; + public static final EnchantRegrowth REGROWTH; + + public static final EnchantBomber BOMBER; + public static final EnchantEnderBow ENDER_BOW; + public static final EnchantGhast GHAST; + public static final EnchantPoisonedArrows POISONED_ARROWS; + public static final EnchantWitheredArrows WITHERED_ARROWS; + public static final EnchantExplosiveArrows EXPLOSIVE_ARROWS; + + static { + PLUGIN = ExcellentEnchants.getInstance(); + PLUGIN.getConfigManager().extract("enchants"); + ENCHANT_LIST = new HashSet<>(); + + // Tool enchants + BLAST_MINING = init(EnchantBlastMining.class, EnchantBlastMining.ID); + CURSE_OF_BREAKING = init(EnchantCurseOfBreaking.class, EnchantCurseOfBreaking.ID); + CURSE_OF_MISFORTUNE = init(EnchantCurseOfMisfortune.class, EnchantCurseOfMisfortune.ID); + DIVINE_TOUCH = init(EnchantDivineTouch.class, EnchantDivineTouch.ID); + HASTE = init(EnchantHaste.class, EnchantHaste.ID); + LUCKY_MINER = init(EnchantLuckyMiner.class, EnchantLuckyMiner.ID); + REPLANTER = init(EnchantReplanter.class, EnchantReplanter.ID); + SILK_CHEST = init(EnchantSilkChest.class, EnchantSilkChest.ID); + SMELTER = init(EnchantSmelter.class, EnchantSmelter.ID); + TELEKINESIS = init(EnchantTelekinesis.class, EnchantTelekinesis.ID); + TREASURES = init(EnchantTreasures.class, EnchantTreasures.ID); + TUNNEL = init(EnchantTunnel.class, EnchantTunnel.ID); + VEINMINER = init(EnchantVeinminer.class, EnchantVeinminer.ID); + + // Weapon enchants + BANE_OF_NETHERSPAWN = init(EnchantBaneOfNetherspawn.class, EnchantBaneOfNetherspawn.ID); + BLINDNESS = init(EnchantBlindness.class, EnchantBlindness.ID); + CONFUSION = init(EnchantConfusion.class, EnchantConfusion.ID); + CUTTER = init(EnchantCutter.class, EnchantCutter.ID); + DECAPITATOR = init(EnchantDecapitator.class, EnchantDecapitator.ID); + DOUBLE_STRIKE = init(EnchantDoubleStrike.class, EnchantDoubleStrike.ID); + EXHAUST = init(EnchantExhaust.class, EnchantExhaust.ID); + EXP_HUNTER = init(EnchantExpHunter.class, EnchantExpHunter.ID); + ICE_ASPECT = init(EnchantIceAspect.class, EnchantIceAspect.ID); + INFERNUS = init(EnchantInfernus.class, EnchantInfernus.ID); + NIMBLE = init(EnchantNimble.class, EnchantNimble.ID); + PARALYZE = init(EnchantParalyze.class, EnchantParalyze.ID); + CURE = init(EnchantCure.class, EnchantCure.ID); + RAGE = init(EnchantRage.class, EnchantRage.ID); + ROCKET = init(EnchantRocket.class, EnchantRocket.ID); + SCAVENGER = init(EnchantScavenger.class, EnchantScavenger.ID); + SURPRISE = init(EnchantSurprise.class, EnchantSurprise.ID); + THRIFTY = init(EnchantThrifty.class, EnchantThrifty.ID); + THUNDER = init(EnchantThunder.class, EnchantThunder.ID); + VAMPIRE = init(EnchantVampire.class, EnchantVampire.ID); + VENOM = init(EnchantVenom.class, EnchantVenom.ID); + VILLAGE_DEFENDER = init(EnchantVillageDefender.class, EnchantVillageDefender.ID); + WITHER = init(EnchantWither.class, EnchantWither.ID); + + // Armor enchants + AQUAMAN = init(EnchantAquaman.class, EnchantAquaman.ID); + BUNNY_HOP = init(EnchantBunnyHop.class, EnchantBunnyHop.ID); + COLD_STEEL = init(EnchantColdSteel.class, EnchantColdSteel.ID); + FLAME_WALKER = init(EnchantFlameWalker.class, EnchantFlameWalker.ID); + HARDENED = init(EnchantHardened.class, EnchantHardened.ID); + NIGHT_VISION = init(EnchantNightVision.class, EnchantNightVision.ID); + REGROWTH = init(EnchantRegrowth.class, EnchantRegrowth.ID); + SATURATION = init(EnchantSaturation.class, EnchantSaturation.ID); + SELF_DESTRUCTION = init(EnchantSelfDestruction.class, EnchantSelfDestruction.ID); + SONIC = init(EnchantSonic.class, EnchantSonic.ID); + + // Bow enchants + BOMBER = init(EnchantBomber.class, EnchantBomber.ID); + ENDER_BOW = init(EnchantEnderBow.class, EnchantEnderBow.ID); + EXPLOSIVE_ARROWS = init(EnchantExplosiveArrows.class, EnchantExplosiveArrows.ID); + GHAST = init(EnchantGhast.class, EnchantGhast.ID); + POISONED_ARROWS = init(EnchantPoisonedArrows.class, EnchantPoisonedArrows.ID); + WITHERED_ARROWS = init(EnchantWitheredArrows.class, EnchantWitheredArrows.ID); + + } + + public static void setup() { + if (ExcellentEnchants.isLoaded) return; // Prevent to register enchantments during the runtime. + + //ENCHANT_LIST.clear(); + Reflex.setFieldValue(Enchantment.class, "acceptingNew", true); + + for (Field field : EnchantRegister.class.getFields()) { + if (!ExcellentEnchant.class.isAssignableFrom(field.getType())) continue; + + ExcellentEnchant enchant; + try { + enchant = (ExcellentEnchant) field.get(null); + EnchantRegister.register(enchant); + } + catch (Exception e) { + e.printStackTrace(); + } + } + + Enchantment.stopAcceptingRegistrations(); + PLUGIN.info("Enchants Registered: " + ENCHANT_LIST.size()); + ExcellentEnchants.isLoaded = true; + } + + @SuppressWarnings("unchecked") + public static void shutdown() { + if (PLUGIN.isEnabled()) return; // Prevent to unregister enchantments during the runtime. + + Map byKey = (Map) Reflex.getFieldValue(Enchantment.class, "byKey"); + Map byName = (Map) Reflex.getFieldValue(Enchantment.class, "byName"); + + if (byKey == null || byName == null) return; + + for (ExcellentEnchant enchant : ENCHANT_LIST) { + if (enchant instanceof ICleanable cleanable) { + cleanable.clear(); + } + + byKey.remove(enchant.getKey()); + byName.remove(enchant.getName()); + enchant.unregisterListeners(); + } + ENCHANT_LIST.clear(); + PLUGIN.info("All enchants are unregistered."); + } + + @Nullable + private static T init(@NotNull Class clazz, @NotNull String id) { + String enchantId = id.toLowerCase(); + if (Config.ENCHANTMENTS_DISABLED.contains(id)) return null; + + JYML enchantCfg = JYML.loadOrExtract(PLUGIN, "/enchants/" + enchantId + ".yml"); + try { + return clazz.getConstructor(ExcellentEnchants.class, JYML.class).newInstance(PLUGIN, enchantCfg); + } + catch (ReflectiveOperationException ex) { + ex.printStackTrace(); + return null; + } + } + + private static void register(@Nullable ExcellentEnchant enchant) { + if (enchant == null) return; + + Enchantment.registerEnchantment(enchant); + ENCHANT_LIST.add(enchant); + enchant.registerListeners(); + PLUGIN.info("Registered enchantment: " + enchant.getId()); + //IRegistry.a(IRegistry.ENCHANTMENT, enchant.getId(), CraftEnchantment.getRaw(enchant)); + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/armor/EnchantAquaman.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/armor/EnchantAquaman.java new file mode 100644 index 0000000..5664bb8 --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/armor/EnchantAquaman.java @@ -0,0 +1,35 @@ +package su.nightexpress.excellentenchants.manager.enchants.armor; + +import org.bukkit.enchantments.EnchantmentTarget; +import org.bukkit.entity.LivingEntity; +import org.bukkit.potion.PotionEffectType; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.EnchantPriority; +import su.nightexpress.excellentenchants.api.enchantment.IEnchantPotionTemplate; +import su.nightexpress.excellentenchants.api.enchantment.type.PassiveEnchant; + +public class EnchantAquaman extends IEnchantPotionTemplate implements PassiveEnchant { + + public static final String ID = "aquaman"; + + public EnchantAquaman(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg) { + super(plugin, cfg, EnchantPriority.MEDIUM, PotionEffectType.WATER_BREATHING); + } + + @Override + @NotNull + public EnchantmentTarget getItemTarget() { + return EnchantmentTarget.ARMOR_HEAD; + } + + @Override + public boolean use(@NotNull LivingEntity entity, int level) { + if (!this.isEnchantmentAvailable(entity)) return false; + if (!this.checkTriggerChance(level)) return false; + if (!this.takeCostItem(entity)) return false; + + return this.addEffect(entity, level); + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/armor/EnchantBunnyHop.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/armor/EnchantBunnyHop.java new file mode 100644 index 0000000..23f57f9 --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/armor/EnchantBunnyHop.java @@ -0,0 +1,35 @@ +package su.nightexpress.excellentenchants.manager.enchants.armor; + +import org.bukkit.enchantments.EnchantmentTarget; +import org.bukkit.entity.LivingEntity; +import org.bukkit.potion.PotionEffectType; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.EnchantPriority; +import su.nightexpress.excellentenchants.api.enchantment.IEnchantPotionTemplate; +import su.nightexpress.excellentenchants.api.enchantment.type.PassiveEnchant; + +public class EnchantBunnyHop extends IEnchantPotionTemplate implements PassiveEnchant { + + public static final String ID = "bunny_hop"; + + public EnchantBunnyHop(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg) { + super(plugin, cfg, EnchantPriority.MEDIUM, PotionEffectType.JUMP); + } + + @Override + @NotNull + public EnchantmentTarget getItemTarget() { + return EnchantmentTarget.ARMOR_FEET; + } + + @Override + public boolean use(@NotNull LivingEntity entity, int level) { + if (!this.isEnchantmentAvailable(entity)) return false; + if (!this.checkTriggerChance(level)) return false; + if (!this.takeCostItem(entity)) return false; + + return this.addEffect(entity, level); + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/armor/EnchantColdSteel.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/armor/EnchantColdSteel.java new file mode 100644 index 0000000..02a79cc --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/armor/EnchantColdSteel.java @@ -0,0 +1,37 @@ +package su.nightexpress.excellentenchants.manager.enchants.armor; + +import org.bukkit.enchantments.EnchantmentTarget; +import org.bukkit.entity.LivingEntity; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.potion.PotionEffectType; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.EnchantPriority; +import su.nightexpress.excellentenchants.api.enchantment.IEnchantPotionTemplate; +import su.nightexpress.excellentenchants.api.enchantment.type.CombatEnchant; + +public class EnchantColdSteel extends IEnchantPotionTemplate implements CombatEnchant { + + public static final String ID = "cold_steel"; + + public EnchantColdSteel(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg) { + super(plugin, cfg, EnchantPriority.MEDIUM, PotionEffectType.SLOW_DIGGING); + } + + @Override + @NotNull + public EnchantmentTarget getItemTarget() { + return EnchantmentTarget.ARMOR_TORSO; + } + + @Override + public boolean use(@NotNull EntityDamageByEntityEvent e, @NotNull LivingEntity damager, @NotNull LivingEntity victim, @NotNull ItemStack weapon, int level) { + if (!this.isEnchantmentAvailable(damager)) return false; + if (!this.checkTriggerChance(level)) return false; + if (!this.takeCostItem(victim)) return false; + + return this.addEffect(damager, level); + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/armor/EnchantFlameWalker.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/armor/EnchantFlameWalker.java new file mode 100644 index 0000000..63f097f --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/armor/EnchantFlameWalker.java @@ -0,0 +1,179 @@ +package su.nightexpress.excellentenchants.manager.enchants.armor; + +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Particle; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.enchantments.EnchantmentTarget; +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.BlockBreakEvent; +import org.bukkit.event.entity.EntityDamageEvent; +import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.inventory.EntityEquipment; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +import su.nexmedia.engine.api.manager.ICleanable; +import su.nexmedia.engine.api.task.AbstractTask; +import su.nexmedia.engine.manager.leveling.Scaler; +import su.nexmedia.engine.utils.EffectUtil; +import su.nexmedia.engine.utils.random.Rnd; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.EnchantPriority; +import su.nightexpress.excellentenchants.api.enchantment.IEnchantChanceTemplate; +import su.nightexpress.excellentenchants.api.enchantment.type.MoveEnchant; +import su.nightexpress.excellentenchants.manager.object.EnchantScaler; + +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Stream; + +public class EnchantFlameWalker extends IEnchantChanceTemplate implements MoveEnchant, ICleanable { + + public static final String ID = "flame_walker"; + + private static final BlockFace[] FACES = {BlockFace.SOUTH, BlockFace.NORTH, BlockFace.EAST, BlockFace.WEST}; + private static final Map BLOCKS_TO_DESTROY = new HashMap<>(); + + private final Scaler blockDecayTime; + private BlockTickTask blockTickTask; + + public EnchantFlameWalker(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg) { + super(plugin, cfg, EnchantPriority.MEDIUM); + + this.blockDecayTime = new EnchantScaler(this, "Settings.Block_Decay"); + + this.blockTickTask = new BlockTickTask(plugin); + this.blockTickTask.start(); + } + + @Override + protected void updateConfig() { + super.updateConfig(); + + cfg.addMissing("Settings.Block_Decay", "5.0 + " + PLACEHOLDER_LEVEL + " * 2"); + } + + @Override + public void clear() { + if (this.blockTickTask != null) { + this.blockTickTask.stop(); + this.blockTickTask = null; + } + BLOCKS_TO_DESTROY.keySet().forEach(block -> block.setType(Material.LAVA)); + BLOCKS_TO_DESTROY.clear(); + } + + public static void addBlock(@NotNull Block block, double seconds) { + BLOCKS_TO_DESTROY.put(block, (long) (System.currentTimeMillis() + seconds * 1000L)); + } + + @Override + protected void addConflicts() { + super.addConflicts(); + this.addConflict(Enchantment.FROST_WALKER); + } + + @Override + @NotNull + public EnchantmentTarget getItemTarget() { + return EnchantmentTarget.ARMOR_FEET; + } + + public double getBlockDecayTime(int level) { + return this.blockDecayTime.getValue(level); + } + + @Override + public boolean use(@NotNull PlayerMoveEvent e, @NotNull LivingEntity entity, int level) { + if (!this.isEnchantmentAvailable(entity)) return false; + if (!this.checkTriggerChance(level)) return false; + if (!this.takeCostItem(entity)) return false; + + plugin.getEnchantNMS().handleFlameWalker(entity, entity.getLocation(), level).forEach(block -> { + addBlock(block, Rnd.getDouble(this.getBlockDecayTime(level)) + 1); + }); + return true; + } + + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onEnchantFlameWalker(PlayerMoveEvent e) { + Player player = e.getPlayer(); + if (player.isFlying()) return; + if (!this.isEnchantmentAvailable(player)) return; + + Location from = e.getFrom(); + Location to = e.getTo(); + if (to == null) return; + if (from.getX() == to.getX() && from.getY() == to.getY() && from.getZ() == to.getZ()) return; + + ItemStack boots = player.getInventory().getBoots(); + if (boots == null || boots.getType().isAir()) return; + + int level = boots.getEnchantmentLevel(this); + if (level < 1) return; + + Block bTo = to.getBlock().getRelative(BlockFace.DOWN); + boolean hasLava = Stream.of(FACES).anyMatch(face -> bTo.getRelative(face).getType() == Material.LAVA); + if (!hasLava) return; + + this.use(e, player, level); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onFlameWalkerBlock(BlockBreakEvent e) { + if (BLOCKS_TO_DESTROY.containsKey(e.getBlock())) { + e.setDropItems(false); + e.setExpToDrop(0); + e.getBlock().setType(Material.LAVA); + } + } + + @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) + public void onFlameWalkerMagmaDamage(EntityDamageEvent e) { + if (e.getCause() != EntityDamageEvent.DamageCause.HOT_FLOOR) return; + if (!(e.getEntity() instanceof LivingEntity livingEntity)) return; + if (!this.isEnchantmentAvailable(livingEntity)) return; + + EntityEquipment equipment = livingEntity.getEquipment(); + if (equipment == null) return; + + ItemStack boots = equipment.getBoots(); + if (boots == null || boots.getType().isAir()) return; + + int level = boots.getEnchantmentLevel(this); + if (level < 1) return; + if (!this.checkTriggerChance(level)) return; + + e.setCancelled(true); + } + + static class BlockTickTask extends AbstractTask { + + public BlockTickTask(@NotNull ExcellentEnchants plugin) { + super(plugin, 1, false); + } + + @Override + public void action() { + long now = System.currentTimeMillis(); + + BLOCKS_TO_DESTROY.keySet().removeIf(block -> { + if (block.isEmpty()) return true; + + long time = BLOCKS_TO_DESTROY.get(block); + if (now >= time) { + block.setType(Material.LAVA); + EffectUtil.playEffect(block.getLocation(), Particle.BLOCK_CRACK.name(), Material.MAGMA_BLOCK.name(), 0.5, 0.7, 0.5, 0.03, 50); + return true; + } + return false; + }); + } + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/armor/EnchantHardened.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/armor/EnchantHardened.java new file mode 100644 index 0000000..74fdbc8 --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/armor/EnchantHardened.java @@ -0,0 +1,37 @@ +package su.nightexpress.excellentenchants.manager.enchants.armor; + +import org.bukkit.enchantments.EnchantmentTarget; +import org.bukkit.entity.LivingEntity; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.potion.PotionEffectType; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.EnchantPriority; +import su.nightexpress.excellentenchants.api.enchantment.IEnchantPotionTemplate; +import su.nightexpress.excellentenchants.api.enchantment.type.CombatEnchant; + +public class EnchantHardened extends IEnchantPotionTemplate implements CombatEnchant { + + public static final String ID = "hardened"; + + public EnchantHardened(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg) { + super(plugin, cfg, EnchantPriority.MEDIUM, PotionEffectType.DAMAGE_RESISTANCE); + } + + @Override + @NotNull + public EnchantmentTarget getItemTarget() { + return EnchantmentTarget.ARMOR_TORSO; + } + + @Override + public boolean use(@NotNull EntityDamageByEntityEvent e, @NotNull LivingEntity damager, @NotNull LivingEntity victim, @NotNull ItemStack weapon, int level) { + if (!this.isEnchantmentAvailable(damager)) return false; + if (!this.checkTriggerChance(level)) return false; + if (!this.takeCostItem(victim)) return false; + + return this.addEffect(victim, level); + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/armor/EnchantNightVision.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/armor/EnchantNightVision.java new file mode 100644 index 0000000..a6eda1d --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/armor/EnchantNightVision.java @@ -0,0 +1,35 @@ +package su.nightexpress.excellentenchants.manager.enchants.armor; + +import org.bukkit.enchantments.EnchantmentTarget; +import org.bukkit.entity.LivingEntity; +import org.bukkit.potion.PotionEffectType; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.EnchantPriority; +import su.nightexpress.excellentenchants.api.enchantment.IEnchantPotionTemplate; +import su.nightexpress.excellentenchants.api.enchantment.type.PassiveEnchant; + +public class EnchantNightVision extends IEnchantPotionTemplate implements PassiveEnchant { + + public static final String ID = "night_vision"; + + public EnchantNightVision(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg) { + super(plugin, cfg, EnchantPriority.MEDIUM, PotionEffectType.NIGHT_VISION); + } + + @Override + @NotNull + public EnchantmentTarget getItemTarget() { + return EnchantmentTarget.ARMOR_HEAD; + } + + @Override + public boolean use(@NotNull LivingEntity entity, int level) { + if (!this.isEnchantmentAvailable(entity)) return false; + if (!this.checkTriggerChance(level)) return false; + if (!this.takeCostItem(entity)) return false; + + return this.addEffect(entity, level); + } +} \ No newline at end of file diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/armor/EnchantRegrowth.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/armor/EnchantRegrowth.java new file mode 100644 index 0000000..9b1bb02 --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/armor/EnchantRegrowth.java @@ -0,0 +1,119 @@ +package su.nightexpress.excellentenchants.manager.enchants.armor; + +import org.bukkit.Particle; +import org.bukkit.attribute.Attribute; +import org.bukkit.enchantments.EnchantmentTarget; +import org.bukkit.entity.LivingEntity; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +import su.nexmedia.engine.api.manager.ICleanable; +import su.nexmedia.engine.manager.leveling.Scaler; +import su.nexmedia.engine.utils.EffectUtil; +import su.nexmedia.engine.utils.EntityUtil; +import su.nexmedia.engine.utils.NumberUtil; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.EnchantPriority; +import su.nightexpress.excellentenchants.api.enchantment.IEnchantChanceTemplate; +import su.nightexpress.excellentenchants.api.enchantment.type.PassiveEnchant; +import su.nightexpress.excellentenchants.manager.object.EnchantScaler; +import su.nightexpress.excellentenchants.manager.tasks.AbstractEnchantPassiveTask; + +import java.util.function.UnaryOperator; + +public class EnchantRegrowth extends IEnchantChanceTemplate implements PassiveEnchant, ICleanable { + + public static final String ID = "regrowth"; + + private final String particleName; + private final String particleData; + private final long healthInterval; + private final Scaler healthAmount; + private Task healthTask; + + private static final String PLACEHOLDER_HEALTH_AMOUNT = "%enchantment_health_amount%"; + private static final String PLACEHOLDER_HEALTH_INTERVAL = "%enchantment_health_interval%"; + + public EnchantRegrowth(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg) { + super(plugin, cfg, EnchantPriority.MEDIUM); + this.particleName = cfg.getString("Settings.Particle.Name", Particle.HEART.name()); + this.particleData = cfg.getString("Settings.Particle.Data", ""); + this.healthInterval = cfg.getLong("Settings.Health.Interval", 100); + this.healthAmount = new EnchantScaler(this, "Settings.Health.Amount"); + this.healthTask = new Task(plugin); + this.healthTask.start(); + } + + @Override + protected void updateConfig() { + super.updateConfig(); + + cfg.addMissing("Settings.Health.Interval", 100); + cfg.addMissing("Settings.Particle.Name", Particle.HEART.name()); + cfg.addMissing("Settings.Particle.Data", ""); + } + + @Override + public void clear() { + if (this.healthTask != null) { + this.healthTask.stop(); + this.healthTask = null; + } + } + + @Override + @NotNull + public UnaryOperator replacePlaceholders(int level) { + return str -> super.replacePlaceholders(level).apply(str + .replace(PLACEHOLDER_HEALTH_AMOUNT, NumberUtil.format(this.getHealthAmount(level))) + .replace(PLACEHOLDER_HEALTH_INTERVAL, NumberUtil.format((double) this.healthInterval / 20D)) + ); + } + + @NotNull + @Override + public EnchantmentTarget getItemTarget() { + return EnchantmentTarget.ARMOR_TORSO; + } + + public double getHealthAmount(int level) { + return this.healthAmount.getValue(level); + } + + public long getHealthInterval() { + return this.healthInterval; + } + + @Override + public boolean use(@NotNull LivingEntity entity, int level) { + if (!this.isEnchantmentAvailable(entity)) return false; + if (!this.checkTriggerChance(level)) return false; + + double healthMax = EntityUtil.getAttribute(entity, Attribute.GENERIC_MAX_HEALTH); + double healthHas = entity.getHealth(); + if (healthHas >= healthMax) return false; + if (!this.takeCostItem(entity)) return false; + + double amount = Math.min(healthMax, healthHas + this.getHealthAmount(level)); + + entity.setHealth(amount); + EffectUtil.playEffect(entity.getEyeLocation(), this.particleName, this.particleData, 0.3, 0.3, 0.3, 0.1, 15); + return true; + } + + class Task extends AbstractEnchantPassiveTask { + + public Task(@NotNull ExcellentEnchants plugin) { + super(plugin, healthInterval, false); + } + + @Override + protected void apply(@NotNull LivingEntity entity, @NotNull ItemStack armor, @NotNull ItemMeta meta) { + int level = meta.getEnchantLevel(EnchantRegrowth.this); + if (level < 1) return; + + use(entity, level); + } + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/armor/EnchantSaturation.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/armor/EnchantSaturation.java new file mode 100644 index 0000000..99b1e0e --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/armor/EnchantSaturation.java @@ -0,0 +1,108 @@ +package su.nightexpress.excellentenchants.manager.enchants.armor; + +import org.bukkit.enchantments.EnchantmentTarget; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +import su.nexmedia.engine.api.manager.ICleanable; +import su.nexmedia.engine.manager.leveling.Scaler; +import su.nexmedia.engine.utils.NumberUtil; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.EnchantPriority; +import su.nightexpress.excellentenchants.api.enchantment.IEnchantChanceTemplate; +import su.nightexpress.excellentenchants.api.enchantment.type.PassiveEnchant; +import su.nightexpress.excellentenchants.manager.object.EnchantScaler; +import su.nightexpress.excellentenchants.manager.tasks.AbstractEnchantPassiveTask; + +import java.util.function.UnaryOperator; + +public class EnchantSaturation extends IEnchantChanceTemplate implements PassiveEnchant, ICleanable { + + private final long saturationInterval; + private final Scaler saturationAmount; + private Task saturationTask; + + public static final String ID = "saturation"; + + private static final String PLACEHOLDER_SATURATION_AMOUNT = "%enchantment_saturation_amount%"; + private static final String PLACEHOLDER_SATURATION_INTERVAL = "%enchantment_saturation_interval%"; + + public EnchantSaturation(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg) { + super(plugin, cfg, EnchantPriority.MEDIUM); + + this.saturationInterval = cfg.getLong("Settings.Saturation.Interval", 100); + this.saturationAmount = new EnchantScaler(this, "Settings.Saturation.Amount"); + this.saturationTask = new Task(plugin); + this.saturationTask.start(); + } + + @Override + protected void updateConfig() { + super.updateConfig(); + + cfg.addMissing("Settings.Saturation.Interval", 100); + } + + @Override + public void clear() { + if (this.saturationTask != null) { + this.saturationTask.stop(); + this.saturationTask = null; + } + } + + @Override + @NotNull + public UnaryOperator replacePlaceholders(int level) { + return str -> super.replacePlaceholders(level).apply(str + .replace(PLACEHOLDER_SATURATION_AMOUNT, NumberUtil.format(this.getSaturationAmount(level))) + .replace(PLACEHOLDER_SATURATION_INTERVAL, NumberUtil.format((double) this.saturationInterval / 20D)) + ); + } + + @Override + @NotNull + public EnchantmentTarget getItemTarget() { + return EnchantmentTarget.ARMOR_TORSO; + } + + public final double getSaturationAmount(int level) { + return this.saturationAmount.getValue(level); + } + + public long getSaturationInterval() { + return saturationInterval; + } + + @Override + public boolean use(@NotNull LivingEntity entity, int level) { + if (!this.isEnchantmentAvailable(entity)) return false; + if (!(entity instanceof Player player)) return false; + if (!this.checkTriggerChance(level)) return false; + if (player.getFoodLevel() >= 20) return false; + if (!this.takeCostItem(player)) return false; + + float amount = (float) this.getSaturationAmount(level); + player.setFoodLevel((int) Math.min(20, player.getFoodLevel() + amount)); + player.setSaturation(Math.min(20, player.getSaturation() + amount)); + return true; + } + + class Task extends AbstractEnchantPassiveTask { + + public Task(@NotNull ExcellentEnchants plugin) { + super(plugin, saturationInterval, true); + } + + @Override + protected void apply(@NotNull LivingEntity entity, @NotNull ItemStack armor, @NotNull ItemMeta meta) { + int level = meta.getEnchantLevel(EnchantSaturation.this); + if (level < 1) return; + + use(entity, level); + } + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/armor/EnchantSelfDestruction.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/armor/EnchantSelfDestruction.java new file mode 100644 index 0000000..ecd3ff0 --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/armor/EnchantSelfDestruction.java @@ -0,0 +1,75 @@ +package su.nightexpress.excellentenchants.manager.enchants.armor; + +import org.bukkit.enchantments.EnchantmentTarget; +import org.bukkit.entity.Item; +import org.bukkit.entity.LivingEntity; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.entity.EntityDeathEvent; +import org.bukkit.metadata.FixedMetadataValue; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +import su.nexmedia.engine.manager.leveling.Scaler; +import su.nexmedia.engine.utils.NumberUtil; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.EnchantPriority; +import su.nightexpress.excellentenchants.api.enchantment.IEnchantChanceTemplate; +import su.nightexpress.excellentenchants.api.enchantment.type.DeathEnchant; +import su.nightexpress.excellentenchants.manager.object.EnchantScaler; + +import java.util.function.UnaryOperator; + +public class EnchantSelfDestruction extends IEnchantChanceTemplate implements DeathEnchant { + + private final Scaler explosionSize; + + public static final String ID = "self_destruction"; + private static final String META_EXPLOSION_SOURCE = ID + "_explosion_source"; + + private static final String PLACEHOLDER_EXPLOSION_POWER = "%enchantment_explosion_power%"; + + public EnchantSelfDestruction(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg) { + super(plugin, cfg, EnchantPriority.MEDIUM); + this.explosionSize = new EnchantScaler(this, "Settings.Explosion.Size"); + } + + @Override + @NotNull + public UnaryOperator replacePlaceholders(int level) { + return str -> super.replacePlaceholders(level).apply(str + .replace(PLACEHOLDER_EXPLOSION_POWER, NumberUtil.format(this.getExplosionSize(level))) + ); + } + + @Override + @NotNull + public EnchantmentTarget getItemTarget() { + return EnchantmentTarget.ARMOR_TORSO; + } + + public final double getExplosionSize(int level) { + return this.explosionSize.getValue(level); + } + + @Override + public boolean use(@NotNull EntityDeathEvent e, @NotNull LivingEntity dead, int level) { + if (!this.isEnchantmentAvailable(dead)) return false; + if (!this.checkTriggerChance(level)) return false; + if (!this.takeCostItem(dead)) return false; + + float size = (float) this.getExplosionSize(level); + dead.setMetadata(META_EXPLOSION_SOURCE, new FixedMetadataValue(plugin, true)); + boolean exploded = dead.getWorld().createExplosion(dead.getLocation(), size, false, false, dead); + dead.removeMetadata(META_EXPLOSION_SOURCE, plugin); + return exploded; + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onItemDamage(EntityDamageByEntityEvent e) { + if (!e.getDamager().hasMetadata(META_EXPLOSION_SOURCE)) return; + if (!(e.getEntity() instanceof Item item)) return; + + e.setCancelled(true); + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/armor/EnchantSonic.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/armor/EnchantSonic.java new file mode 100644 index 0000000..4e34205 --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/armor/EnchantSonic.java @@ -0,0 +1,35 @@ +package su.nightexpress.excellentenchants.manager.enchants.armor; + +import org.bukkit.enchantments.EnchantmentTarget; +import org.bukkit.entity.LivingEntity; +import org.bukkit.potion.PotionEffectType; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.EnchantPriority; +import su.nightexpress.excellentenchants.api.enchantment.IEnchantPotionTemplate; +import su.nightexpress.excellentenchants.api.enchantment.type.PassiveEnchant; + +public class EnchantSonic extends IEnchantPotionTemplate implements PassiveEnchant { + + public static final String ID = "sonic"; + + public EnchantSonic(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg) { + super(plugin, cfg, EnchantPriority.MEDIUM, PotionEffectType.SPEED); + } + + @Override + @NotNull + public EnchantmentTarget getItemTarget() { + return EnchantmentTarget.ARMOR_FEET; + } + + @Override + public boolean use(@NotNull LivingEntity entity, int level) { + if (!this.isEnchantmentAvailable(entity)) return false; + if (!this.checkTriggerChance(level)) return false; + if (!this.takeCostItem(entity)) return false; + + return this.addEffect(entity, level); + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/bow/EnchantBomber.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/bow/EnchantBomber.java new file mode 100644 index 0000000..d16f31f --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/bow/EnchantBomber.java @@ -0,0 +1,100 @@ +package su.nightexpress.excellentenchants.manager.enchants.bow; + +import org.bukkit.enchantments.Enchantment; +import org.bukkit.enchantments.EnchantmentTarget; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Projectile; +import org.bukkit.entity.TNTPrimed; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.entity.EntityShootBowEvent; +import org.bukkit.event.entity.ProjectileHitEvent; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +import su.nexmedia.engine.manager.leveling.Scaler; +import su.nexmedia.engine.utils.NumberUtil; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.EnchantPriority; +import su.nightexpress.excellentenchants.api.enchantment.IEnchantChanceTemplate; +import su.nightexpress.excellentenchants.api.enchantment.type.BowEnchant; +import su.nightexpress.excellentenchants.manager.EnchantRegister; +import su.nightexpress.excellentenchants.manager.object.EnchantScaler; + +import java.util.function.UnaryOperator; + +public class EnchantBomber extends IEnchantChanceTemplate implements BowEnchant { + + private final Scaler fuseTicks; + + public static final String ID = "bomber"; + + public static final String PLACEHOLDER_FUSE_TICKS = "%enchantment_fuse_ticks%"; + + public EnchantBomber(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg) { + super(plugin, cfg, EnchantPriority.HIGHEST); + + this.fuseTicks = new EnchantScaler(this, "Settings.Fuse_Ticks"); + } + + @Override + protected void updateConfig() { + super.updateConfig(); + this.cfg.addMissing("Settings.Fuse_Ticks", "100 - %enchantment_level% * 10"); + } + + @Override + @NotNull + public UnaryOperator replacePlaceholders(int level) { + return str -> super.replacePlaceholders(level).apply(str + .replace(PLACEHOLDER_FUSE_TICKS, NumberUtil.format((double) this.getFuseTicks(level) / 20D)) + ); + } + + public int getFuseTicks(int level) { + return (int) this.fuseTicks.getValue(level); + } + + @Override + protected void addConflicts() { + super.addConflicts(); + this.addConflict(EnchantRegister.ENDER_BOW); + this.addConflict(EnchantRegister.GHAST); + this.addConflict(EnchantRegister.EXPLOSIVE_ARROWS); + this.addConflict(EnchantRegister.WITHERED_ARROWS); + this.addConflict(EnchantRegister.POISONED_ARROWS); + this.addConflict(Enchantment.ARROW_FIRE); + this.addConflict(Enchantment.ARROW_DAMAGE); + this.addConflict(Enchantment.ARROW_KNOCKBACK); + } + + @Override + @NotNull + public EnchantmentTarget getItemTarget() { + return EnchantmentTarget.BOW; + } + + @Override + public boolean use(@NotNull EntityShootBowEvent e, @NotNull LivingEntity shooter, @NotNull ItemStack bow, int level) { + if (!this.isEnchantmentAvailable(shooter)) return false; + if (!this.checkTriggerChance(level)) return false; + if (!(e.getProjectile() instanceof Projectile projectile)) return false; + if (!bow.containsEnchantment(ARROW_INFINITE) && !this.takeCostItem(shooter)) return false; + + TNTPrimed primed = projectile.getWorld().spawn(projectile.getLocation(), TNTPrimed.class); + primed.setVelocity(projectile.getVelocity().multiply(e.getForce() * 1.25)); + primed.setFuseTicks(this.getFuseTicks(level)); + primed.setSource(shooter); + e.setProjectile(primed); + return true; + } + + @Override + public boolean use(@NotNull ProjectileHitEvent e, @NotNull Projectile projectile, @NotNull ItemStack bow, int level) { + return false; + } + + @Override + public boolean use(@NotNull EntityDamageByEntityEvent e, @NotNull LivingEntity damager, @NotNull LivingEntity victim, @NotNull ItemStack weapon, int level) { + return false; + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/bow/EnchantEnderBow.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/bow/EnchantEnderBow.java new file mode 100644 index 0000000..8c6b9df --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/bow/EnchantEnderBow.java @@ -0,0 +1,69 @@ +package su.nightexpress.excellentenchants.manager.enchants.bow; + +import org.bukkit.enchantments.Enchantment; +import org.bukkit.enchantments.EnchantmentTarget; +import org.bukkit.entity.EnderPearl; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Projectile; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.entity.EntityShootBowEvent; +import org.bukkit.event.entity.ProjectileHitEvent; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.EnchantPriority; +import su.nightexpress.excellentenchants.api.enchantment.IEnchantChanceTemplate; +import su.nightexpress.excellentenchants.api.enchantment.type.BowEnchant; +import su.nightexpress.excellentenchants.manager.EnchantRegister; + +public class EnchantEnderBow extends IEnchantChanceTemplate implements BowEnchant { + + public static final String ID = "ender_bow"; + + public EnchantEnderBow(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg) { + super(plugin, cfg, EnchantPriority.HIGHEST); + } + + @Override + protected void addConflicts() { + super.addConflicts(); + this.addConflict(EnchantRegister.BOMBER); + this.addConflict(EnchantRegister.GHAST); + this.addConflict(EnchantRegister.EXPLOSIVE_ARROWS); + this.addConflict(EnchantRegister.WITHERED_ARROWS); + this.addConflict(EnchantRegister.POISONED_ARROWS); + this.addConflict(Enchantment.ARROW_FIRE); + this.addConflict(Enchantment.ARROW_DAMAGE); + this.addConflict(Enchantment.ARROW_KNOCKBACK); + } + + @Override + @NotNull + public EnchantmentTarget getItemTarget() { + return EnchantmentTarget.BOW; + } + + @Override + public boolean use(@NotNull EntityShootBowEvent e, @NotNull LivingEntity shooter, @NotNull ItemStack bow, int level) { + if (!this.isEnchantmentAvailable(shooter)) return false; + if (!this.checkTriggerChance(level)) return false; + if (!(e.getProjectile() instanceof Projectile projectile)) return false; + if (!bow.containsEnchantment(ARROW_INFINITE) && !this.takeCostItem(shooter)) return false; + + EnderPearl pearl = shooter.launchProjectile(EnderPearl.class); + pearl.setVelocity(projectile.getVelocity()); + e.setProjectile(pearl); + return true; + } + + @Override + public boolean use(@NotNull ProjectileHitEvent e, @NotNull Projectile projectile, @NotNull ItemStack bow, int level) { + return false; + } + + @Override + public boolean use(@NotNull EntityDamageByEntityEvent e, @NotNull LivingEntity damager, @NotNull LivingEntity victim, @NotNull ItemStack weapon, int level) { + return false; + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/bow/EnchantExplosiveArrows.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/bow/EnchantExplosiveArrows.java new file mode 100644 index 0000000..bbb5fb9 --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/bow/EnchantExplosiveArrows.java @@ -0,0 +1,102 @@ +package su.nightexpress.excellentenchants.manager.enchants.bow; + +import org.bukkit.World; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Item; +import org.bukkit.entity.Projectile; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.entity.EntityDamageEvent; +import org.bukkit.event.entity.ProjectileHitEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.metadata.FixedMetadataValue; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +import su.nexmedia.engine.manager.leveling.Scaler; +import su.nexmedia.engine.utils.NumberUtil; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.EnchantPriority; +import su.nightexpress.excellentenchants.api.enchantment.IEnchantBowTemplate; +import su.nightexpress.excellentenchants.manager.object.EnchantScaler; + +import java.util.function.UnaryOperator; + +public class EnchantExplosiveArrows extends IEnchantBowTemplate { + + private final boolean explosionFireSpread; + private final boolean explosionDamageItems; + private final boolean explosionDamageBlocks; + private final Scaler explosionSize; + + public static final String ID = "explosive_arrows"; + public static final String PLACEHOLDER_EXPLOSION_POWER = "%enchantment_explosion_power%"; + + private static final String META_EXPLOSION_SOURCE = ID + "_source"; + + public EnchantExplosiveArrows(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg) { + super(plugin, cfg, EnchantPriority.MEDIUM); + this.explosionFireSpread = cfg.getBoolean("Settings.Explosion.Fire_Spread"); + this.explosionDamageItems = cfg.getBoolean("Settings.Explosion.Damage_Items"); + this.explosionDamageBlocks = cfg.getBoolean("Settings.Explosion.Damage_Blocks"); + this.explosionSize = new EnchantScaler(this, "Settings.Explosion.Size"); + } + + @Override + protected void updateConfig() { + super.updateConfig(); + + this.cfg.addMissing("Settings.Explosion.Fire_Spread", true); + this.cfg.addMissing("Settings.Explosion.Damage_Items", true); + this.cfg.addMissing("Settings.Explosion.Damage_Blocks", false); + } + + @Override + @NotNull + public UnaryOperator replacePlaceholders(int level) { + return str -> super.replacePlaceholders(level).apply(str + .replace(PLACEHOLDER_EXPLOSION_POWER, NumberUtil.format(this.getExplosionSize(level))) + ); + } + + public final double getExplosionSize(int level) { + return this.explosionSize.getValue(level); + } + + public final boolean isExplosionFireSpread() { + return this.explosionFireSpread; + } + + public final boolean isExplosionDamageBlocks() { + return this.explosionDamageBlocks; + } + + @Override + public boolean use(@NotNull ProjectileHitEvent e, @NotNull Projectile projectile, @NotNull ItemStack bow, int level) { + if (!super.use(e, projectile, bow, level)) return false; + + Entity shooter = null; + if (projectile.getShooter() instanceof Entity entity) { + shooter = entity; + shooter.setMetadata(META_EXPLOSION_SOURCE, new FixedMetadataValue(this.plugin, true)); + } + + World world = projectile.getWorld(); + float explSize = (float) this.getExplosionSize(level); + boolean explFire = this.isExplosionFireSpread(); + boolean explBlocks = this.isExplosionDamageBlocks(); + boolean exploded = world.createExplosion(projectile.getLocation(), explSize, explFire, explBlocks, shooter); + if (shooter != null) shooter.removeMetadata(META_EXPLOSION_SOURCE, this.plugin); + return exploded; + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onItemDamage(EntityDamageByEntityEvent e) { + 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); + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/bow/EnchantGhast.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/bow/EnchantGhast.java new file mode 100644 index 0000000..5997474 --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/bow/EnchantGhast.java @@ -0,0 +1,112 @@ +package su.nightexpress.excellentenchants.manager.enchants.bow; + +import org.bukkit.enchantments.Enchantment; +import org.bukkit.enchantments.EnchantmentTarget; +import org.bukkit.entity.Fireball; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Projectile; +import org.bukkit.entity.SmallFireball; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.entity.EntityShootBowEvent; +import org.bukkit.event.entity.ProjectileHitEvent; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +import su.nexmedia.engine.manager.leveling.Scaler; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.EnchantPriority; +import su.nightexpress.excellentenchants.api.enchantment.IEnchantChanceTemplate; +import su.nightexpress.excellentenchants.api.enchantment.type.BowEnchant; +import su.nightexpress.excellentenchants.manager.EnchantRegister; +import su.nightexpress.excellentenchants.manager.object.EnchantScaler; + +public class EnchantGhast extends IEnchantChanceTemplate implements BowEnchant { + + private final boolean fireSpread; + private final Scaler yield; + + public static final String ID = "ghast"; + + public EnchantGhast(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg) { + super(plugin, cfg, EnchantPriority.HIGHEST); + + this.fireSpread = cfg.getBoolean("Settings.Fire_Spread"); + this.yield = new EnchantScaler(this, "Settings.Yield"); + } + + @Override + protected void updateConfig() { + super.updateConfig(); + + cfg.addMissing("Settings.Fire_Spread", true); + cfg.addMissing("Settings.Yield", "1.0 * " + PLACEHOLDER_LEVEL); + } + + @Override + protected void addConflicts() { + super.addConflicts(); + this.addConflict(EnchantRegister.BOMBER); + this.addConflict(EnchantRegister.ENDER_BOW); + this.addConflict(EnchantRegister.EXPLOSIVE_ARROWS); + this.addConflict(EnchantRegister.WITHERED_ARROWS); + this.addConflict(EnchantRegister.POISONED_ARROWS); + this.addConflict(Enchantment.ARROW_FIRE); + this.addConflict(Enchantment.ARROW_KNOCKBACK); + } + + public boolean isFireSpread() { + return fireSpread; + } + + public float getYield(int level) { + return (float) this.yield.getValue(level); + } + + @Override + @NotNull + public EnchantmentTarget getItemTarget() { + return EnchantmentTarget.BOW; + } + + @Override + public boolean use(@NotNull EntityShootBowEvent e, @NotNull LivingEntity shooter, @NotNull ItemStack bow, int level) { + if (!this.isEnchantmentAvailable(shooter)) return false; + if (!this.checkTriggerChance(level)) return false; + if (!(e.getProjectile() instanceof Projectile projectile)) return false; + if (!bow.containsEnchantment(ARROW_INFINITE) && !this.takeCostItem(shooter)) return false; + + Fireball fireball; + + // Shoot small fireballs for the Multishot enchantment, + // as large ones has a slow speed and punches each other on shoot. + if (bow.containsEnchantment(Enchantment.MULTISHOT)) { + fireball = shooter.launchProjectile(SmallFireball.class); + fireball.setVelocity(projectile.getVelocity().normalize().multiply(0.5f)); + } + else { + fireball = shooter.launchProjectile(Fireball.class); + fireball.setDirection(projectile.getVelocity()); + } + fireball.setIsIncendiary(this.isFireSpread()); + fireball.setYield(this.getYield(level)); + + e.setProjectile(fireball); + return true; + } + + @Override + public boolean use(@NotNull ProjectileHitEvent e, @NotNull Projectile projectile, @NotNull ItemStack bow, int level) { + return false; + } + + @Override + public boolean use(@NotNull EntityDamageByEntityEvent e, @NotNull LivingEntity damager, @NotNull LivingEntity victim, @NotNull ItemStack weapon, int level) { + // Support for the 'Power' enchantment. + int power = weapon.getEnchantmentLevel(Enchantment.ARROW_DAMAGE); + if (power < 1) return false; + + double damagePower = 0.5 + power * 0.5; + e.setDamage(e.getDamage() + damagePower); + return true; + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/bow/EnchantPoisonedArrows.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/bow/EnchantPoisonedArrows.java new file mode 100644 index 0000000..bf4527c --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/bow/EnchantPoisonedArrows.java @@ -0,0 +1,17 @@ +package su.nightexpress.excellentenchants.manager.enchants.bow; + +import org.bukkit.potion.PotionEffectType; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.EnchantPriority; +import su.nightexpress.excellentenchants.api.enchantment.IEnchantBowPotionTemplate; + +public class EnchantPoisonedArrows extends IEnchantBowPotionTemplate { + + public static final String ID = "poisoned_arrows"; + + public EnchantPoisonedArrows(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg) { + super(plugin, cfg, EnchantPriority.MEDIUM, PotionEffectType.POISON); + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/bow/EnchantWitheredArrows.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/bow/EnchantWitheredArrows.java new file mode 100644 index 0000000..ab6cb4e --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/bow/EnchantWitheredArrows.java @@ -0,0 +1,17 @@ +package su.nightexpress.excellentenchants.manager.enchants.bow; + +import org.bukkit.potion.PotionEffectType; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.EnchantPriority; +import su.nightexpress.excellentenchants.api.enchantment.IEnchantBowPotionTemplate; + +public class EnchantWitheredArrows extends IEnchantBowPotionTemplate { + + public static final String ID = "withered_arrows"; + + public EnchantWitheredArrows(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg) { + super(plugin, cfg, EnchantPriority.MEDIUM, PotionEffectType.WITHER); + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/tool/EnchantBlastMining.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/tool/EnchantBlastMining.java new file mode 100644 index 0000000..a3de7be --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/tool/EnchantBlastMining.java @@ -0,0 +1,165 @@ +package su.nightexpress.excellentenchants.manager.enchants.tool; + +import org.bukkit.block.Block; +import org.bukkit.enchantments.EnchantmentTarget; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.entity.EntityDamageEvent.DamageCause; +import org.bukkit.event.entity.EntityExplodeEvent; +import org.bukkit.event.player.PlayerItemDamageEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.metadata.FixedMetadataValue; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +import su.nexmedia.engine.manager.leveling.Scaler; +import su.nexmedia.engine.utils.NumberUtil; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.EnchantPriority; +import su.nightexpress.excellentenchants.api.enchantment.IEnchantChanceTemplate; +import su.nightexpress.excellentenchants.api.enchantment.type.BlockBreakEnchant; +import su.nightexpress.excellentenchants.hook.HookNCP; +import su.nightexpress.excellentenchants.manager.EnchantRegister; +import su.nightexpress.excellentenchants.manager.object.EnchantScaler; +import su.nightexpress.excellentenchants.manager.type.FitItemType; + +import java.util.List; +import java.util.function.UnaryOperator; + +public class EnchantBlastMining extends IEnchantChanceTemplate implements BlockBreakEnchant { + + private final Scaler explosionPower; + private final Scaler minBlockStrength; + + public static final String ID = "blast_mining"; + public static final String PLACEHOLDER_EXPLOSION_POWER = "%enchantment_explosion_power%"; + + private static final String META_EXPLOSION_SOURCE = ID + "_explosion_source"; + private static final String META_EXPLOSION_MINED = ID + "_explosion_mined"; + + public EnchantBlastMining(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg) { + super(plugin, cfg, EnchantPriority.MEDIUM); + + this.explosionPower = new EnchantScaler(this, "Settings.Explosion.Power"); + this.minBlockStrength = new EnchantScaler(this, "Settings.Min_Block_Strength"); + } + + public double getExplosionPower(int level) { + return this.explosionPower.getValue(level); + } + + public float getMinBlockStrength(int level) { + return (float) minBlockStrength.getValue(level); + } + + private boolean isBlockHardEnough(@NotNull Block block, int level) { + float strength = plugin.getNMS().getBlockStrength(block); + return (strength >= this.getMinBlockStrength(level)); + } + + @Override + protected void updateConfig() { + super.updateConfig(); + + cfg.addMissing("Settings.Min_Block_Strength", "1.5 - " + PLACEHOLDER_LEVEL + " / 10.0"); + } + + @Override + protected void addConflicts() { + super.addConflicts(); + this.addConflict(EnchantRegister.TUNNEL); + this.addConflict(EnchantRegister.VEINMINER); + } + + @Override + @NotNull + public UnaryOperator replacePlaceholders(int level) { + return str -> super.replacePlaceholders(level).apply(str + .replace(PLACEHOLDER_EXPLOSION_POWER, NumberUtil.format(this.getExplosionPower(level))) + ); + } + + /*@Override + public boolean isFitItemType(@NotNull ItemStack item) { + return ItemUtil.isPickaxe(item); + }*/ + + @Override + @NotNull + public FitItemType[] getFitItemTypes() { + return new FitItemType[]{FitItemType.PICKAXE}; + } + + @Override + @NotNull + public EnchantmentTarget getItemTarget() { + return EnchantmentTarget.TOOL; + } + + @Override + public boolean use(@NotNull BlockBreakEvent e, @NotNull Player player, @NotNull ItemStack item, int level) { + if (!this.isEnchantmentAvailable(player)) return false; + + if (EnchantRegister.VEINMINER != null && item.containsEnchantment(EnchantRegister.VEINMINER)) return false; + if (EnchantRegister.TUNNEL != null && item.containsEnchantment(EnchantRegister.TUNNEL)) return false; + + Block block = e.getBlock(); + if (block.hasMetadata(META_EXPLOSION_MINED)) return false; + + if (!this.isBlockHardEnough(block, level)) return false; + if (!this.checkTriggerChance(level)) return false; + if (!this.takeCostItem(player)) return false; + + float power = (float) this.getExplosionPower(level); + + player.setMetadata(META_EXPLOSION_SOURCE, new FixedMetadataValue(plugin, level)); + HookNCP.exemptBlocks(player); + boolean exploded = block.getWorld().createExplosion(block.getLocation(), power, false, true, player); + HookNCP.unexemptBlocks(player); + player.removeMetadata(META_EXPLOSION_SOURCE, plugin); + return exploded; + } + + // Process explosion event to mine blocks. + @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) + public void onBlastExplosionEvent(EntityExplodeEvent e) { + if (!(e.getEntity() instanceof Player player)) return; + if (!player.hasMetadata(META_EXPLOSION_SOURCE)) return; + + int level = player.getMetadata(META_EXPLOSION_SOURCE).get(0).asInt(); + List blockList = e.blockList(); + + // Remove the 'source' block which player mined and caused the explosion to prevent duplicated drops. + // Remove all the 'soft' blocks that should not be exploded. + blockList.removeIf(block -> block.getLocation().equals(e.getLocation()) || !this.isBlockHardEnough(block, level)); + + // Break all 'exploded' blocks by a player, adding metadata to them to prevent trigger enchantment in a loop. + blockList.forEach(block -> { + block.setMetadata(META_EXPLOSION_MINED, new FixedMetadataValue(plugin, true)); + plugin.getNMS().breakBlock(player, block); + block.removeMetadata(META_EXPLOSION_MINED, plugin); + }); + + // Clear list of 'exploded' blocks so the event won't affect them, as they are already mined by a player. + blockList.clear(); + } + + // Do not damage around entities by en enchantment explosion. + @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) + public void onBlastExplosionDamage(EntityDamageByEntityEvent e) { + if (e.getCause() != DamageCause.ENTITY_EXPLOSION) return; + if (!(e.getDamager() instanceof Player player)) return; + + e.setCancelled(player.hasMetadata(META_EXPLOSION_SOURCE)); + } + + // Do not reduce item durability for 'exploded' blocks. + @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) + public void onBlastExplosionItemDamage(PlayerItemDamageEvent e) { + if (!e.getPlayer().hasMetadata(META_EXPLOSION_SOURCE)) return; + + e.setCancelled(true); + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/tool/EnchantCurseOfBreaking.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/tool/EnchantCurseOfBreaking.java new file mode 100644 index 0000000..c2d219b --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/tool/EnchantCurseOfBreaking.java @@ -0,0 +1,75 @@ +package su.nightexpress.excellentenchants.manager.enchants.tool; + +import org.bukkit.enchantments.Enchantment; +import org.bukkit.enchantments.EnchantmentTarget; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.player.PlayerItemDamageEvent; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +import su.nexmedia.engine.manager.leveling.Scaler; +import su.nexmedia.engine.utils.NumberUtil; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.EnchantPriority; +import su.nightexpress.excellentenchants.api.enchantment.IEnchantChanceTemplate; +import su.nightexpress.excellentenchants.manager.object.EnchantScaler; + +import java.util.function.UnaryOperator; + +public class EnchantCurseOfBreaking extends IEnchantChanceTemplate { + + private final Scaler durabilityAmount; + + public static final String ID = "curse_of_breaking"; + public static final String PLACEHOLDER_DURABILITY_AMOUNT = "%enchantment_durability_amount%"; + + public EnchantCurseOfBreaking(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg) { + super(plugin, cfg, EnchantPriority.MEDIUM); + + this.durabilityAmount = new EnchantScaler(this, "Settings.Durability_Amount"); + } + + public int getDurabilityAmount(int level) { + return (int) this.durabilityAmount.getValue(level); + } + + @Override + protected void addConflicts() { + super.addConflicts(); + this.addConflict(Enchantment.DURABILITY); + } + + @Override + @NotNull + public UnaryOperator replacePlaceholders(int level) { + return str -> super.replacePlaceholders(level).apply(str + .replace(PLACEHOLDER_DURABILITY_AMOUNT, NumberUtil.format(this.getDurabilityAmount(level))) + ); + } + + @NotNull + @Override + public EnchantmentTarget getItemTarget() { + return EnchantmentTarget.BREAKABLE; + } + + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onItemDurability(PlayerItemDamageEvent e) { + Player player = e.getPlayer(); + if (!this.isEnchantmentAvailable(player)) return; + + ItemStack item = e.getItem(); + int level = item.getEnchantmentLevel(this); + + if (level < 1) return; + if (!this.checkTriggerChance(level)) return; + if (!this.takeCostItem(player)) return; + + int durabilityAmount = this.getDurabilityAmount(level); + if (durabilityAmount <= 0) return; + + e.setDamage(e.getDamage() + durabilityAmount); + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/tool/EnchantCurseOfMisfortune.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/tool/EnchantCurseOfMisfortune.java new file mode 100644 index 0000000..b9066c4 --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/tool/EnchantCurseOfMisfortune.java @@ -0,0 +1,83 @@ +package su.nightexpress.excellentenchants.manager.enchants.tool; + +import org.bukkit.enchantments.EnchantmentTarget; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.entity.EntityDeathEvent; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.EnchantPriority; +import su.nightexpress.excellentenchants.api.enchantment.IEnchantChanceTemplate; +import su.nightexpress.excellentenchants.api.enchantment.type.BlockBreakEnchant; +import su.nightexpress.excellentenchants.api.enchantment.type.DeathEnchant; +import su.nightexpress.excellentenchants.manager.type.FitItemType; + +public class EnchantCurseOfMisfortune extends IEnchantChanceTemplate implements BlockBreakEnchant, DeathEnchant { + + private final boolean dropExp; + + public static final String ID = "curse_of_misfortune"; + + public EnchantCurseOfMisfortune(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg) { + super(plugin, cfg, EnchantPriority.LOW); + + this.dropExp = cfg.getBoolean("Settings.Drop_Exp"); + } + + public boolean isDropExp() { + return dropExp; + } + + @Override + protected void addConflicts() { + super.addConflicts(); + this.addConflict(LOOT_BONUS_BLOCKS); + this.addConflict(LOOT_BONUS_MOBS); + } + + @Override + @NotNull + public FitItemType[] getFitItemTypes() { + return new FitItemType[] {FitItemType.WEAPON, FitItemType.TOOL}; + } + + @NotNull + @Override + public EnchantmentTarget getItemTarget() { + return EnchantmentTarget.BREAKABLE; + } + + @Override + public boolean isCursed() { + return true; + } + + @Override + public boolean use(@NotNull BlockBreakEvent e, @NotNull Player player, @NotNull ItemStack item, int level) { + if (!this.isEnchantmentAvailable(player)) return false; + if (EnchantTelekinesis.isDropHandled(e.getBlock())) return false; + if (!this.checkTriggerChance(level)) return false; + if (!this.takeCostItem(player)) return false; + + e.setDropItems(false); + if (!this.isDropExp()) e.setExpToDrop(0); + return true; + } + + @Override + public boolean use(@NotNull EntityDeathEvent e, @NotNull LivingEntity dead, int level) { + Player player = dead.getKiller(); + if (player == null) return false; + if (!this.isEnchantmentAvailable(player)) return false; + + if (!this.checkTriggerChance(level)) return false; + if (!this.takeCostItem(player)) return false; + + e.getDrops().clear(); + if (!this.isDropExp()) e.setDroppedExp(0); + return true; + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/tool/EnchantDivineTouch.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/tool/EnchantDivineTouch.java new file mode 100644 index 0000000..bb4ee1f --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/tool/EnchantDivineTouch.java @@ -0,0 +1,150 @@ +package su.nightexpress.excellentenchants.manager.enchants.tool; + +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Particle; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.CreatureSpawner; +import org.bukkit.enchantments.EnchantmentTarget; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.BlockStateMeta; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +import su.nexmedia.engine.utils.EffectUtil; +import su.nexmedia.engine.utils.LocationUtil; +import su.nexmedia.engine.utils.StringUtil; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.EnchantPriority; +import su.nightexpress.excellentenchants.api.enchantment.IEnchantChanceTemplate; +import su.nightexpress.excellentenchants.api.enchantment.type.BlockBreakEnchant; +import su.nightexpress.excellentenchants.api.enchantment.type.CustomDropEnchant; +import su.nightexpress.excellentenchants.manager.EnchantRegister; +import su.nightexpress.excellentenchants.manager.type.FitItemType; + +import java.util.Collections; +import java.util.List; + +public class EnchantDivineTouch extends IEnchantChanceTemplate implements BlockBreakEnchant, CustomDropEnchant { + + private final String particleName; + private final String particleData; + private final String spawnerName; + + public static final String ID = "divine_touch"; + + public EnchantDivineTouch(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg) { + super(plugin, cfg, EnchantPriority.MEDIUM); + + this.particleName = cfg.getString("Settings.Particle.Name", Particle.VILLAGER_HAPPY.name()); + this.particleData = cfg.getString("Settings.Particle.Data", ""); + this.spawnerName = StringUtil.color(cfg.getString("Settings.Spawner_Item.Name", "&aMob Spawner &7(%type%)")); + } + + @Override + protected void addConflicts() { + super.addConflicts(); + this.addConflict(EnchantRegister.SMELTER); + } + + @Override + protected void updateConfig() { + super.updateConfig(); + + cfg.remove("Settings.Particle_Effect"); + cfg.addMissing("Settings.Particle.Name", Particle.VILLAGER_HAPPY.name()); + cfg.addMissing("Settings.Particle.Data", ""); + } + + @Override + @NotNull + public FitItemType[] getFitItemTypes() { + return new FitItemType[]{FitItemType.PICKAXE}; + } + + @Override + @NotNull + public EnchantmentTarget getItemTarget() { + return EnchantmentTarget.TOOL; + } + + @NotNull + public ItemStack getSpawner(@NotNull CreatureSpawner spawnerBlock) { + ItemStack itemSpawner = new ItemStack(Material.SPAWNER); + BlockStateMeta stateItem = (BlockStateMeta) itemSpawner.getItemMeta(); + if (stateItem == null) return itemSpawner; + + CreatureSpawner spawnerItem = (CreatureSpawner) stateItem.getBlockState(); + spawnerItem.setSpawnedType(spawnerBlock.getSpawnedType()); + spawnerItem.update(true); + stateItem.setBlockState(spawnerItem); + stateItem.setDisplayName(this.spawnerName.replace("%type%", plugin.lang().getEnum(spawnerBlock.getSpawnedType()))); + itemSpawner.setItemMeta(stateItem); + + return itemSpawner; + } + + @Override + @NotNull + public List getCustomDrops(@NotNull Player player, @NotNull ItemStack item, @NotNull Block block, int level) { + if (!(block.getState() instanceof CreatureSpawner spawnerBlock)) return Collections.emptyList(); + + return Collections.singletonList(this.getSpawner(spawnerBlock)); + } + + @Override + public boolean isEventMustHaveDrops() { + return false; + } + + @Override + public boolean use(@NotNull BlockBreakEvent e, @NotNull Player player, @NotNull ItemStack item, int level) { + if (!this.isEnchantmentAvailable(player)) return false; + Block block = e.getBlock(); + if (EnchantTelekinesis.isDropHandled(block)) return false; + if (!(block.getState() instanceof CreatureSpawner spawnerBlock)) return false; + if (this.isEventMustHaveDrops() && !e.isDropItems()) return false; + if (!this.checkTriggerChance(level)) return false; + if (!this.takeCostItem(player)) return false; + + Location location = LocationUtil.getCenter(block.getLocation()); + World world = block.getWorld(); + + this.getCustomDrops(player, item, block, level).forEach(itemSpawner -> world.dropItemNaturally(location, itemSpawner)); + EffectUtil.playEffect(location, this.particleName, this.particleData, 0.3f, 0.3f, 0.3f, 0.15f, 30); + + e.setExpToDrop(0); + e.setDropItems(false); + return true; + } + + // Update spawner type of the placed spawner mined by Divine Touch. + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onSpawnerPlace(BlockPlaceEvent e) { + Block block = e.getBlock(); + if (block.getType() != Material.SPAWNER) return; + + Player player = e.getPlayer(); + ItemStack spawner = player.getInventory().getItemInMainHand(); + if (spawner.getType().isAir() || spawner.getType() != Material.SPAWNER) { + spawner = player.getInventory().getItemInOffHand(); + } + if (spawner.getType().isAir() || spawner.getType() != Material.SPAWNER) { + return; + } + + BlockStateMeta meta = (BlockStateMeta) spawner.getItemMeta(); + if (meta == null) return; + + CreatureSpawner spawnerItem = (CreatureSpawner) meta.getBlockState(); + CreatureSpawner spawnerBlock = (CreatureSpawner) block.getState(); + + spawnerBlock.setSpawnedType(spawnerItem.getSpawnedType()); + spawnerBlock.update(); + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/tool/EnchantHaste.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/tool/EnchantHaste.java new file mode 100644 index 0000000..26eb8ba --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/tool/EnchantHaste.java @@ -0,0 +1,35 @@ +package su.nightexpress.excellentenchants.manager.enchants.tool; + +import org.bukkit.enchantments.EnchantmentTarget; +import org.bukkit.entity.LivingEntity; +import org.bukkit.potion.PotionEffectType; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.EnchantPriority; +import su.nightexpress.excellentenchants.api.enchantment.IEnchantPotionTemplate; +import su.nightexpress.excellentenchants.api.enchantment.type.PassiveEnchant; + +public class EnchantHaste extends IEnchantPotionTemplate implements PassiveEnchant { + + public static final String ID = "haste"; + + public EnchantHaste(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg) { + super(plugin, cfg, EnchantPriority.MEDIUM, PotionEffectType.FAST_DIGGING); + } + + @Override + @NotNull + public EnchantmentTarget getItemTarget() { + return EnchantmentTarget.TOOL; + } + + @Override + public boolean use(@NotNull LivingEntity entity, int level) { + if (!this.isEnchantmentAvailable(entity)) return false; + if (!this.checkTriggerChance(level)) return false; + if (!this.takeCostItem(entity)) return false; + + return this.addEffect(entity, level); + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/tool/EnchantLuckyMiner.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/tool/EnchantLuckyMiner.java new file mode 100644 index 0000000..a712b6e --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/tool/EnchantLuckyMiner.java @@ -0,0 +1,73 @@ +package su.nightexpress.excellentenchants.manager.enchants.tool; + +import org.bukkit.enchantments.EnchantmentTarget; +import org.bukkit.entity.Player; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +import su.nexmedia.engine.manager.leveling.Scaler; +import su.nexmedia.engine.utils.NumberUtil; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.EnchantPriority; +import su.nightexpress.excellentenchants.api.enchantment.IEnchantChanceTemplate; +import su.nightexpress.excellentenchants.api.enchantment.type.BlockBreakEnchant; +import su.nightexpress.excellentenchants.manager.object.EnchantScaler; +import su.nightexpress.excellentenchants.manager.type.FitItemType; + +import java.util.function.UnaryOperator; + +public class EnchantLuckyMiner extends IEnchantChanceTemplate implements BlockBreakEnchant { + + private final Scaler expModifier; + + public static final String ID = "lucky_miner"; + + private static final String PLACEHOLDER_EXP_MODIFIER = "%enchantment_exp_modifier%"; + + public EnchantLuckyMiner(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg) { + super(plugin, cfg, EnchantPriority.MEDIUM); + this.expModifier = new EnchantScaler(this, "Settings.Exp_Modifier"); + } + + public double getExpModifier(int level) { + return this.expModifier.getValue(level); + } + + @Override + protected void updateConfig() { + super.updateConfig(); + } + + @Override + @NotNull + public UnaryOperator replacePlaceholders(int level) { + return str -> super.replacePlaceholders(level).apply(str + .replace(PLACEHOLDER_EXP_MODIFIER, NumberUtil.format(this.getExpModifier(level) * 100D - 100D)) + ); + } + + @Override + @NotNull + public FitItemType[] getFitItemTypes() { + return new FitItemType[]{FitItemType.PICKAXE}; + } + + @Override + @NotNull + public EnchantmentTarget getItemTarget() { + return EnchantmentTarget.TOOL; + } + + @Override + public boolean use(@NotNull BlockBreakEvent e, @NotNull Player player, @NotNull ItemStack item, int level) { + if (e.isCancelled()) return false; + if (!this.isEnchantmentAvailable(player)) return false; + if (!this.checkTriggerChance(level)) return false; + if (!this.takeCostItem(player)) return false; + + double expMod = this.getExpModifier(level); + e.setExpToDrop((int) ((double) e.getExpToDrop() * expMod)); + return true; + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/tool/EnchantReplanter.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/tool/EnchantReplanter.java new file mode 100644 index 0000000..ed11133 --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/tool/EnchantReplanter.java @@ -0,0 +1,182 @@ +package su.nightexpress.excellentenchants.manager.enchants.tool; + +import com.google.common.collect.Sets; +import org.bukkit.Material; +import org.bukkit.Sound; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.data.Ageable; +import org.bukkit.block.data.BlockData; +import org.bukkit.enchantments.EnchantmentTarget; +import org.bukkit.entity.Player; +import org.bukkit.event.block.Action; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.EquipmentSlot; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +import su.nexmedia.engine.utils.MessageUtil; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.EnchantPriority; +import su.nightexpress.excellentenchants.api.enchantment.IEnchantChanceTemplate; +import su.nightexpress.excellentenchants.api.enchantment.type.BlockBreakEnchant; +import su.nightexpress.excellentenchants.api.enchantment.type.InteractEnchant; +import su.nightexpress.excellentenchants.manager.type.FitItemType; + +import java.util.Set; + +public class EnchantReplanter extends IEnchantChanceTemplate implements InteractEnchant, BlockBreakEnchant { + + private final boolean replantOnRightClick; + private final boolean replantOnPlantBreak; + + public static final String ID = "replanter"; + + private static final Set CROPS = Sets.newHashSet( + Material.WHEAT_SEEDS, Material.BEETROOT_SEEDS, Material.MELON_SEEDS, Material.PUMPKIN_SEEDS, + Material.POTATO, Material.CARROT, Material.NETHER_WART); + + public EnchantReplanter(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg) { + super(plugin, cfg, EnchantPriority.HIGH); + + this.replantOnRightClick = cfg.getBoolean("Settings.Replant.On_Right_Click"); + this.replantOnPlantBreak = cfg.getBoolean("Settings.Replant.On_Plant_Break"); + } + + public boolean isReplantOnPlantBreak() { + return replantOnPlantBreak; + } + + public boolean isReplantOnRightClick() { + return replantOnRightClick; + } + + @NotNull + private Material fineSeedsToBlock(@NotNull Material material) { + if (material == Material.POTATO) return Material.POTATOES; + if (material == Material.CARROT) return Material.CARROTS; + if (material == Material.BEETROOT_SEEDS) return Material.BEETROOTS; + if (material == Material.WHEAT_SEEDS) return Material.WHEAT; + if (material == Material.PUMPKIN_SEEDS) return Material.PUMPKIN_STEM; + if (material == Material.MELON_SEEDS) return Material.MELON_STEM; + return material; + } + + @NotNull + private Material fineBlockToSeeds(@NotNull Material material) { + if (material == Material.POTATOES) return Material.POTATO; + if (material == Material.CARROTS) return Material.CARROT; + if (material == Material.BEETROOTS) return Material.BEETROOT_SEEDS; + if (material == Material.WHEAT) return Material.WHEAT_SEEDS; + if (material == Material.MELON_STEM) return Material.MELON_SEEDS; + if (material == Material.PUMPKIN_STEM) return Material.PUMPKIN_SEEDS; + return material; + } + + private boolean takeSeeds(@NotNull Player player, @NotNull Material material) { + material = this.fineBlockToSeeds(material); + + int slot = player.getInventory().first(material); + if (slot < 0) return false; + + ItemStack seed = player.getInventory().getItem(slot); + if (seed == null || seed.getType().isAir()) return false; + + seed.setAmount(seed.getAmount() - 1); + return true; + } + + @Override + protected void updateConfig() { + super.updateConfig(); + } + + @Override + @NotNull + public FitItemType[] getFitItemTypes() { + return new FitItemType[]{FitItemType.HOE}; + } + + @Override + @NotNull + public EnchantmentTarget getItemTarget() { + return EnchantmentTarget.TOOL; + } + + @Override + public boolean use(@NotNull PlayerInteractEvent e, @NotNull Player player, @NotNull ItemStack item, int level) { + if (!this.isEnchantmentAvailable(player)) return false; + if (!this.isReplantOnRightClick()) return false; + + // Check for a event hand. We dont want to trigger it twice. + if (e.getHand() != EquipmentSlot.HAND) return false; + if (e.getAction() != Action.RIGHT_CLICK_BLOCK) return false; + + // Check if player holds seeds to plant them by offhand interaction. + ItemStack off = player.getInventory().getItemInOffHand(); + if (!off.getType().isAir() && CROPS.contains(off.getType())) return false; + + // Check if clicked block is a farmland. + Block blockGround = e.getClickedBlock(); + if (blockGround == null) return false; + if (blockGround.getType() != Material.FARMLAND && blockGround.getType() != Material.SOUL_SAND) return false; + + // Check enchantment trigger chance. + if (!this.checkTriggerChance(level)) return false; + + // Check if someting is already growing on the farmland. + Block blockPlant = blockGround.getRelative(BlockFace.UP); + if (!blockPlant.isEmpty()) return false; + + // Get the first crops from player's inventory and plant them. + for (Material seed : CROPS) { + if (seed == Material.NETHER_WART && blockGround.getType() == Material.SOUL_SAND + || seed != Material.NETHER_WART && blockGround.getType() == Material.FARMLAND) { + if (this.takeSeeds(player, seed)) { + MessageUtil.sound(player, seed == Material.NETHER_WART ? Sound.ITEM_NETHER_WART_PLANT : Sound.ITEM_CROP_PLANT); + plugin.getNMS().sendAttackPacket(player, 0); + blockPlant.setType(this.fineSeedsToBlock(seed)); + break; + } + } + } + return true; + } + + @Override + public boolean use(@NotNull BlockBreakEvent e, @NotNull Player player, @NotNull ItemStack item, int level) { + if (!this.isEnchantmentAvailable(player)) return false; + if (!this.isReplantOnPlantBreak()) return false; + + Block blockPlant = e.getBlock(); + //if (EnchantTelekinesis.isDropHandled(blockPlant)) return false; + //if (EnchantRegister.TELEKINESIS != null && item.containsEnchantment(EnchantRegister.TELEKINESIS)) return false; + + // Check if broken block is supported crop(s). + if (!CROPS.contains(this.fineBlockToSeeds(blockPlant.getType()))) return false; + + // Check if broken block is actually can grow. + BlockData dataPlant = blockPlant.getBlockData(); + if (!(dataPlant instanceof Ageable plant)) return false; + + // Check enchantment trigger chance. + if (!this.checkTriggerChance(level)) return false; + + // Check if crop is not at its maximal age to prevent accidient replant. + /*if (plant.getAge() < plant.getMaximumAge()) { + e.setCancelled(true); + return false; + }*/ + + // Replant the gathered crops with a new one. + if (this.takeSeeds(player, plant.getMaterial())) { + plugin.getServer().getScheduler().runTask(plugin, () -> { + blockPlant.setType(plant.getMaterial()); + plant.setAge(0); + blockPlant.setBlockData(plant); + }); + } + return true; + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/tool/EnchantSilkChest.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/tool/EnchantSilkChest.java new file mode 100644 index 0000000..bfa073a --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/tool/EnchantSilkChest.java @@ -0,0 +1,183 @@ +package su.nightexpress.excellentenchants.manager.enchants.tool; + +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.block.Block; +import org.bukkit.block.BlockState; +import org.bukkit.block.Chest; +import org.bukkit.enchantments.EnchantmentTarget; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockPlaceEvent; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryPickupItemEvent; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +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.api.enchantment.EnchantPriority; +import su.nightexpress.excellentenchants.api.enchantment.IEnchantChanceTemplate; +import su.nightexpress.excellentenchants.api.enchantment.type.BlockBreakEnchant; +import su.nightexpress.excellentenchants.api.enchantment.type.CustomDropEnchant; +import su.nightexpress.excellentenchants.manager.type.FitItemType; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +public class EnchantSilkChest extends IEnchantChanceTemplate implements BlockBreakEnchant, CustomDropEnchant { + + private final Map keyItems; + private final String chestName; + + public static final String ID = "silk_chest"; + + public EnchantSilkChest(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg) { + super(plugin, cfg, EnchantPriority.MEDIUM); + this.keyItems = new TreeMap<>(); + this.chestName = StringUtil.color(cfg.getString("Settings.Chest_Item.Name", "%name% &7(%items% items)")); + + for (int pos = 0; pos < 27; pos++) { + this.getItemKey(pos); + } + } + + @Override + protected void updateConfig() { + super.updateConfig(); + } + + @Override + @NotNull + public FitItemType[] getFitItemTypes() { + return new FitItemType[]{FitItemType.AXE}; + } + + @Override + @NotNull + public EnchantmentTarget getItemTarget() { + return EnchantmentTarget.TOOL; + } + + private NamespacedKey getItemKey(int pos) { + return this.keyItems.computeIfAbsent(pos, key -> new NamespacedKey(plugin, "silkchest_item_" + pos)); + } + + public boolean isSilkChest(@NotNull ItemStack item) { + return PDCUtil.getStringData(item, this.getItemKey(0)) != null; + } + + @NotNull + public ItemStack getSilkChest(@NotNull Chest chest) { + Block block = chest.getBlock(); + ItemStack chestItem = new ItemStack(block.getType()); + + // Store and count chest items. + int amount = 0; + int count = 0; + for (ItemStack itemInv : chest.getBlockInventory().getContents()) { + if (itemInv == null) itemInv = new ItemStack(Material.AIR); + else amount++; + + String base64 = ItemUtil.toBase64(itemInv); + if (base64 == null) continue; + if (base64.length() >= Short.MAX_VALUE) { + block.getWorld().dropItemNaturally(block.getLocation(), itemInv); + continue; + } + PDCUtil.setData(chestItem, this.getItemKey(count++), base64); + } + + // Apply item meta name and items data string. + ItemMeta meta = chestItem.getItemMeta(); + if (meta != null) { + String nameOrig = ItemUtil.getItemName(chestItem); + String nameChest = this.chestName.replace("%name%", nameOrig).replace("%items%", String.valueOf(amount)); + meta.setDisplayName(nameChest); + chestItem.setItemMeta(meta); + } + + return chestItem; + } + + @Override + @NotNull + public List getCustomDrops(@NotNull Player player, @NotNull ItemStack item, @NotNull Block block, int level) { + if (block.getType() == Material.ENDER_CHEST) return Collections.emptyList(); + if (!(block.getState() instanceof Chest chest)) return Collections.emptyList(); + + return Collections.singletonList(this.getSilkChest(chest)); + } + + @Override + public boolean isEventMustHaveDrops() { + return true; + } + + @Override + public boolean use(@NotNull BlockBreakEvent e, @NotNull Player player, @NotNull ItemStack item, int level) { + Block block = e.getBlock(); + if (!this.isEnchantmentAvailable(player)) return false; + if (EnchantTelekinesis.isDropHandled(block)) return false; + if (block.getType() == Material.ENDER_CHEST) return false; + if (this.isEventMustHaveDrops() && !e.isDropItems()) return false; + if (!this.checkTriggerChance(level)) return false; + if (!(block.getState() instanceof Chest chest)) return false; + if (!this.takeCostItem(player)) return false; + + // Drop custom chest and do not drop the original one. + this.getCustomDrops(player, item, block, level).forEach(chestItem -> block.getWorld().dropItemNaturally(block.getLocation(), chestItem)); + + // Do not drop chest items. + chest.getBlockInventory().clear(); + e.setDropItems(false); + + return true; + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onSilkChestPlace(BlockPlaceEvent e) { + ItemStack item = e.getItemInHand(); + if (item.getType().isAir()) return; + + Block block = e.getBlockPlaced(); + BlockState state = block.getState(); + if (!(state instanceof Chest chest)) return; + + Inventory inventory = chest.getBlockInventory(); + for (int pos = 0; pos < inventory.getSize(); pos++) { + String data = PDCUtil.getStringData(item, this.getItemKey(pos)); + if (data == null) continue; + + ItemStack itemInv = ItemUtil.fromBase64(data); + inventory.setItem(pos, itemInv); + } + } + + @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) + public void onSilkChestStore(InventoryClickEvent e) { + Inventory inv = e.getInventory(); + if (inv.getType() == InventoryType.CRAFTING || inv.getType() == InventoryType.CREATIVE) return; + + ItemStack item = e.getCurrentItem(); + if (item == null || item.getType().isAir()) return; + + if (this.isSilkChest(item)) { + e.setCancelled(true); + } + } + + @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) + public void onSilkChestHopper(InventoryPickupItemEvent e) { + e.setCancelled(this.isSilkChest(e.getItem().getItemStack())); + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/tool/EnchantSmelter.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/tool/EnchantSmelter.java new file mode 100644 index 0000000..3ba3fa3 --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/tool/EnchantSmelter.java @@ -0,0 +1,144 @@ +package su.nightexpress.excellentenchants.manager.enchants.tool; + +import org.bukkit.*; +import org.bukkit.block.Block; +import org.bukkit.block.Container; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.enchantments.EnchantmentTarget; +import org.bukkit.entity.Player; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +import su.nexmedia.engine.utils.EffectUtil; +import su.nexmedia.engine.utils.LocationUtil; +import su.nexmedia.engine.utils.MessageUtil; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.EnchantPriority; +import su.nightexpress.excellentenchants.api.enchantment.IEnchantChanceTemplate; +import su.nightexpress.excellentenchants.api.enchantment.type.BlockBreakEnchant; +import su.nightexpress.excellentenchants.api.enchantment.type.CustomDropEnchant; +import su.nightexpress.excellentenchants.manager.EnchantRegister; +import su.nightexpress.excellentenchants.manager.type.FitItemType; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class EnchantSmelter extends IEnchantChanceTemplate implements BlockBreakEnchant, CustomDropEnchant { + + private final String sound; + private final String particleName; + private final String particleData; + private final Map smeltingTable; + + public static final String ID = "smelter"; + + public EnchantSmelter(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg) { + super(plugin, cfg, EnchantPriority.MEDIUM); + + this.sound = cfg.getString("Settings.Sound", ""); + this.particleName = cfg.getString("Settings.Particle.Name", Particle.FLAME.name()); + this.particleData = cfg.getString("Settings.Particle.Data", ""); + this.smeltingTable = new HashMap<>(); + for (String sFrom : cfg.getSection("Settings.Smelting_Table")) { + Material mFrom = Material.getMaterial(sFrom.toUpperCase()); + if (mFrom == null) { + plugin.error("[Smelter] Invalid source material '" + sFrom + "' !"); + continue; + } + String sTo = cfg.getString("Settings.Smelting_Table." + sFrom, ""); + Material mTo = Material.getMaterial(sTo.toUpperCase()); + if (mTo == null) { + plugin.error("[Smelter] Invalid result material '" + sTo + "' !"); + continue; + } + this.smeltingTable.put(mFrom, mTo); + } + } + + @Override + protected void addConflicts() { + super.addConflicts(); + this.addConflict(Enchantment.SILK_TOUCH); + this.addConflict(EnchantRegister.DIVINE_TOUCH); + } + + @Override + protected void updateConfig() { + super.updateConfig(); + + cfg.remove("Settings.Particle_Effect"); + cfg.addMissing("Settings.Sound", Sound.BLOCK_LAVA_EXTINGUISH.name()); + cfg.addMissing("Settings.Particle.Name", Particle.FLAME.name()); + cfg.addMissing("Settings.Particle.Data", ""); + } + + @Override + @NotNull + public FitItemType[] getFitItemTypes() { + return new FitItemType[]{FitItemType.PICKAXE, FitItemType.AXE, FitItemType.SHOVEL}; + } + + @Override + @NotNull + public EnchantmentTarget getItemTarget() { + return EnchantmentTarget.TOOL; + } + + @Override + @NotNull + public List getCustomDrops(@NotNull Player player, @NotNull ItemStack item, @NotNull Block block, int level) { + if (block.getState() instanceof Container) return Collections.emptyList(); + + List drops = plugin.getNMS().getBlockDrops(block, player, item); + return this.smelt(drops); + } + + public boolean isSmeltable(@NotNull Material material) { + return this.smeltingTable.containsKey(material); + } + + @NotNull + public List smelt(@NotNull List drops) { + return drops.stream().peek(drop -> { + Material material = this.smeltingTable.get(drop.getType()); + if (material != null) drop.setType(material); + }).toList(); + } + + public void playEffect(@NotNull Block block) { + Location location = LocationUtil.getCenter(block.getLocation(), true); + MessageUtil.sound(location, this.sound); + EffectUtil.playEffect(location, this.particleName, this.particleData, 0.2f, 0.2f, 0.2f, 0.05f, 30); + } + + @Override + public boolean isEventMustHaveDrops() { + return true; + } + + @Override + public boolean use(@NotNull BlockBreakEvent e, @NotNull Player player, @NotNull ItemStack item, int level) { + Block block = e.getBlock(); + if (!this.isEnchantmentAvailable(player)) return false; + if (EnchantTelekinesis.isDropHandled(block)) return false; + if (this.isEventMustHaveDrops() && !e.isDropItems()) return false; + if (!this.checkTriggerChance(level)) return false; + + List defaults = plugin.getNMS().getBlockDrops(block, player, item); + List custom = this.getCustomDrops(player, item, block, level); + if (custom.isEmpty() || custom.containsAll(defaults)) return false; + if (!this.takeCostItem(player)) return false; + + e.setDropItems(false); + + World world = block.getWorld(); + Location location = LocationUtil.getCenter(block.getLocation(), true); + + custom.forEach(itemSmelt -> world.dropItem(location, itemSmelt)); + this.playEffect(block); + return true; + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/tool/EnchantTelekinesis.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/tool/EnchantTelekinesis.java new file mode 100644 index 0000000..cb13694 --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/tool/EnchantTelekinesis.java @@ -0,0 +1,151 @@ +package su.nightexpress.excellentenchants.manager.enchants.tool; + +import org.bukkit.block.Block; +import org.bukkit.block.Chest; +import org.bukkit.block.Container; +import org.bukkit.enchantments.EnchantmentTarget; +import org.bukkit.entity.Player; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.metadata.FixedMetadataValue; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +import su.nexmedia.engine.api.config.LangMessage; +import su.nexmedia.engine.utils.ItemUtil; +import su.nexmedia.engine.utils.PlayerUtil; +import su.nexmedia.engine.utils.StringUtil; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.EnchantPriority; +import su.nightexpress.excellentenchants.api.enchantment.IEnchantChanceTemplate; +import su.nightexpress.excellentenchants.api.enchantment.type.BlockBreakEnchant; +import su.nightexpress.excellentenchants.api.enchantment.type.CustomDropEnchant; +import su.nightexpress.excellentenchants.manager.EnchantManager; +import su.nightexpress.excellentenchants.manager.EnchantRegister; +import su.nightexpress.excellentenchants.manager.type.FitItemType; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.UnaryOperator; + +public class EnchantTelekinesis extends IEnchantChanceTemplate implements BlockBreakEnchant { + + public static final String META_BLOCK_DROP_HANDLER = "telekinesis_drop_handler"; + + private final LangMessage messageDropReceived; + private final String messageItemName; + private final String messageItemSeparator; + + public static final String ID = "telekinesis"; + + public EnchantTelekinesis(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg) { + super(plugin, cfg, EnchantPriority.HIGHEST); + + this.messageDropReceived = new LangMessage(plugin.lang(), cfg.getString("Settings.Message.Drop_Received", "")); + this.messageItemName = StringUtil.color(cfg.getString("Settings.Message.Item_Name", "&7x%item_amount% &f%item_name%")); + this.messageItemSeparator = StringUtil.color(cfg.getString("Settings.Message.Item_Separator", "&7, ")); + } + + @Override + protected void updateConfig() { + super.updateConfig(); + + cfg.remove("Settings.Radius"); + cfg.remove("Settings.Power"); + + cfg.addMissing("Settings.Message.Drop_Received", "{message: ~type: ACTION_BAR; ~prefix: false;}%items%"); + cfg.addMissing("Settings.Message.Item_Name", "&7x%item_amount% &f%item_name%"); + cfg.addMissing("Settings.Message.Item_Separator", "&7, "); + } + + public static boolean isDropHandled(@NotNull Block block) { + return block.hasMetadata(META_BLOCK_DROP_HANDLER); + } + + @Override + @NotNull + public UnaryOperator replacePlaceholders(int level) { + return super.replacePlaceholders(level); + } + + @Override + @NotNull + public FitItemType[] getFitItemTypes() { + return new FitItemType[]{FitItemType.TOOL}; + } + + @Override + @NotNull + public EnchantmentTarget getItemTarget() { + return EnchantmentTarget.TOOL; + } + + @Override + public boolean use(@NotNull BlockBreakEvent e, @NotNull Player player, @NotNull ItemStack item, int level) { + if (!this.isEnchantmentAvailable(player)) return false; + if (e.getBlock().getState() instanceof Container) return false; + if (!e.isDropItems()) return false; + if (!this.checkTriggerChance(level)) return false; + + EnchantCurseOfMisfortune curseOfMisfortune = EnchantRegister.CURSE_OF_MISFORTUNE; + if (curseOfMisfortune != null && item.containsEnchantment(curseOfMisfortune)) { + if (curseOfMisfortune.use(e, player, item, level)) { + return false; + } + } + + Block block = e.getBlock(); + List drops = new ArrayList<>(plugin.getNMS().getBlockDrops(block, player, item)); + + // Check inventory space. + if (drops.stream().anyMatch(itemDrop -> PlayerUtil.countItemSpace(player, itemDrop) == 0)) return false; + + // Tell other enchantments that block drops are handled by Telekinesis. + block.setMetadata(META_BLOCK_DROP_HANDLER, new FixedMetadataValue(plugin, true)); + + for (Map.Entry entry : EnchantManager.getItemCustomEnchants(item, CustomDropEnchant.class).entrySet()) { + CustomDropEnchant dropEnchant = entry.getKey(); + int dropLevel = entry.getValue(); + if (dropEnchant.isEventMustHaveDrops() && !e.isDropItems()) continue; + + if (dropEnchant instanceof IEnchantChanceTemplate chanceEnchant) { + if (!chanceEnchant.checkTriggerChance(dropLevel)) continue; + } + if (dropEnchant instanceof EnchantSilkChest && block.getState() instanceof Chest) { + drops.removeIf(drop -> drop.getType() == block.getType()); + } + if (dropEnchant instanceof EnchantSmelter smelter) { + boolean isSmelted = drops.stream().anyMatch(drop -> smelter.isSmeltable(drop.getType())); + smelter.smelt(drops); + if (isSmelted) smelter.playEffect(block); + continue; // Do not add smelted items twice, only replace current ones. + } + if (dropEnchant instanceof EnchantTreasures treasures) { + if (treasures.getTreasure(block) != null) { + treasures.playEffect(block); + } + } + + drops.addAll(dropEnchant.getCustomDrops(player, item, block, dropLevel)); + } + drops.removeIf(Objects::isNull); + + StringBuilder builder = new StringBuilder(); + drops.forEach(drop -> { + PlayerUtil.addItem(player, drop); + + if (!builder.isEmpty()) builder.append(this.messageItemSeparator); + builder.append(this.messageItemName + .replace("%item_name%", ItemUtil.getItemName(drop)) + .replace("%item_amount%", String.valueOf(drop.getAmount()))); + }); + this.messageDropReceived.replace("%items%", builder.toString()).send(player); + + e.setDropItems(false); + plugin.getServer().getScheduler().runTask(plugin, c -> { + block.removeMetadata(META_BLOCK_DROP_HANDLER, plugin); + }); + return true; + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/tool/EnchantTreasures.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/tool/EnchantTreasures.java new file mode 100644 index 0000000..841185d --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/tool/EnchantTreasures.java @@ -0,0 +1,154 @@ +package su.nightexpress.excellentenchants.manager.enchants.tool; + +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Particle; +import org.bukkit.Sound; +import org.bukkit.block.Block; +import org.bukkit.enchantments.EnchantmentTarget; +import org.bukkit.entity.Player; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import su.nexmedia.engine.NexEngine; +import su.nexmedia.engine.api.config.JYML; +import su.nexmedia.engine.api.manager.ICleanable; +import su.nexmedia.engine.manager.player.listener.PlayerBlockPlacedListener; +import su.nexmedia.engine.utils.EffectUtil; +import su.nexmedia.engine.utils.LocationUtil; +import su.nexmedia.engine.utils.MessageUtil; +import su.nexmedia.engine.utils.random.Rnd; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.EnchantPriority; +import su.nightexpress.excellentenchants.api.enchantment.IEnchantChanceTemplate; +import su.nightexpress.excellentenchants.api.enchantment.type.BlockBreakEnchant; +import su.nightexpress.excellentenchants.api.enchantment.type.CustomDropEnchant; +import su.nightexpress.excellentenchants.manager.type.FitItemType; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Predicate; + +public class EnchantTreasures extends IEnchantChanceTemplate implements BlockBreakEnchant, CustomDropEnchant, ICleanable { + + private final String particleName; + private final String particleData; + private final String sound; + private final Map> treasures; + private final Predicate userBlockFilter; + + public static final String ID = "treasures"; + + public EnchantTreasures(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg) { + super(plugin, cfg, EnchantPriority.LOWEST); + + this.particleName = cfg.getString("Settings.Particle.Name", Particle.REDSTONE.name()); + this.particleData = cfg.getString("Settings.Particle.Data", "200,180,0"); + this.sound = cfg.getString("Settings.Sound", Sound.BLOCK_NOTE_BLOCK_BELL.name()); + this.treasures = new HashMap<>(); + for (String sFromArray : cfg.getSection("Settings.Treasures")) { + for (String sFrom : sFromArray.split(",")) { + Material mFrom = Material.getMaterial(sFrom.toUpperCase()); + if (mFrom == null) { + plugin.error("[Treasures] Invalid source material '" + sFrom + "' !"); + continue; + } + Map treasuresList = new HashMap<>(); + + for (String sTo : cfg.getSection("Settings.Treasures." + sFromArray)) { + Material mTo = Material.getMaterial(sTo.toUpperCase()); + if (mTo == null) { + plugin.error("[Treasures] Invalid result material '" + sTo + "' for '" + sFromArray + "' !"); + continue; + } + + double tChance = cfg.getDouble("Settings.Treasures." + sFromArray + "." + sTo); + treasuresList.put(mTo, tChance); + } + this.treasures.put(mFrom, treasuresList); + } + } + + NexEngine.get().getPlayerManager().enableUserBlockListening(); + PlayerBlockPlacedListener.BLOCK_FILTERS.add(this.userBlockFilter = (block) -> { + return this.getTreasure(block) != null; + }); + } + + @Override + public void clear() { + PlayerBlockPlacedListener.BLOCK_FILTERS.remove(this.userBlockFilter); + } + + @Override + protected void updateConfig() { + super.updateConfig(); + cfg.remove("Settings.Particle_Effect"); + cfg.addMissing("Settings.Particle.Name", Particle.REDSTONE.name()); + cfg.addMissing("Settings.Particle.Data", "200,180,0"); + cfg.addMissing("Settings.Sound", Sound.BLOCK_NOTE_BLOCK_BELL.name()); + } + + @Override + @NotNull + public FitItemType[] getFitItemTypes() { + return new FitItemType[]{FitItemType.PICKAXE, FitItemType.AXE, FitItemType.SHOVEL}; + } + + @Override + @NotNull + public EnchantmentTarget getItemTarget() { + return EnchantmentTarget.TOOL; + } + + @Override + @NotNull + public List getCustomDrops(@NotNull Player player, @NotNull ItemStack item, @NotNull Block block, int level) { + ItemStack drop = this.getTreasure(block); + if (PlayerBlockPlacedListener.isUserPlaced(block) || drop == null) return Collections.emptyList(); + return Collections.singletonList(drop); + } + + @Override + public boolean isEventMustHaveDrops() { + return false; + } + + @Nullable + public final ItemStack getTreasure(@NotNull Block block) { + Map treasures = this.treasures.get(block.getType()); + if (treasures == null) return null; + + Material mat = Rnd.get(treasures); + return mat != null && !mat.isAir() ? new ItemStack(mat) : null; + } + + public void playEffect(@NotNull Block block) { + Location location = LocationUtil.getCenter(block.getLocation()); + MessageUtil.sound(location, this.sound); + EffectUtil.playEffect(location, this.particleName, this.particleData, 0.2f, 0.2f, 0.2f, 0.12f, 20); + } + + + @Override + public boolean use(@NotNull BlockBreakEvent e, @NotNull Player player, @NotNull ItemStack item, int level) { + Block block = e.getBlock(); + if (!this.isEnchantmentAvailable(player)) return false; + if (EnchantTelekinesis.isDropHandled(block)) return false; + if (this.isEventMustHaveDrops() && !e.isDropItems()) return false; + if (PlayerBlockPlacedListener.isUserPlaced(block)) return false; + if (!this.checkTriggerChance(level)) return false; + if (!this.takeCostItem(player)) return false; + + Location location = LocationUtil.getCenter(block.getLocation()); + List drops = this.getCustomDrops(player, item, block, level); + if (drops.isEmpty()) return false; + + drops.forEach(itemDrop -> block.getWorld().dropItem(location, itemDrop)); + this.playEffect(block); + return true; + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/tool/EnchantTunnel.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/tool/EnchantTunnel.java new file mode 100644 index 0000000..fd3416a --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/tool/EnchantTunnel.java @@ -0,0 +1,138 @@ +package su.nightexpress.excellentenchants.manager.enchants.tool; + +import org.bukkit.Material; +import org.bukkit.Particle; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.enchantments.EnchantmentTarget; +import org.bukkit.entity.Player; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.metadata.FixedMetadataValue; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.Version; +import su.nexmedia.engine.api.config.JYML; +import su.nexmedia.engine.utils.EffectUtil; +import su.nexmedia.engine.utils.LocationUtil; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.EnchantPriority; +import su.nightexpress.excellentenchants.api.enchantment.IEnchantChanceTemplate; +import su.nightexpress.excellentenchants.api.enchantment.type.BlockBreakEnchant; +import su.nightexpress.excellentenchants.hook.HookNCP; +import su.nightexpress.excellentenchants.manager.EnchantRegister; +import su.nightexpress.excellentenchants.manager.type.FitItemType; + +import java.util.HashSet; +import java.util.Set; + +public class EnchantTunnel extends IEnchantChanceTemplate implements BlockBreakEnchant { + + private final boolean disableOnSneak; + + public static final String ID = "tunnel"; + private static final String META_BLOCK_TUNNEL = ID + "_block_tunneled"; + // X and Z offsets for each block AoE mined + private static final int[][] MINING_COORD_OFFSETS = new int[][]{{0, 0}, {0, -1}, {-1, 0}, {0, 1}, {1, 0}, {-1, -1}, {-1, 1}, {1, -1}, {1, 1},}; + private static final Set INTERACTABLE_BLOCKS = new HashSet<>(); + + static { + INTERACTABLE_BLOCKS.add(Material.REDSTONE_ORE); + if (Version.CURRENT.isHigher(Version.V1_16_R3)) { + INTERACTABLE_BLOCKS.add(Material.DEEPSLATE_REDSTONE_ORE); + } + } + + public EnchantTunnel(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg) { + super(plugin, cfg, EnchantPriority.HIGH); + this.disableOnSneak = cfg.getBoolean("Settings.Ignore_When_Sneaking"); + } + + @Override + protected void updateConfig() { + super.updateConfig(); + } + + @Override + protected void addConflicts() { + super.addConflicts(); + this.addConflict(EnchantRegister.VEINMINER); + this.addConflict(EnchantRegister.BLAST_MINING); + + } + + @Override + @NotNull + public FitItemType[] getFitItemTypes() { + return new FitItemType[]{FitItemType.PICKAXE, FitItemType.SHOVEL}; + } + + @Override + @NotNull + public EnchantmentTarget getItemTarget() { + return EnchantmentTarget.TOOL; + } + + @Override + public boolean use(@NotNull BlockBreakEvent e, @NotNull Player player, @NotNull ItemStack item, int level) { + Block block = e.getBlock(); + if (!this.isEnchantmentAvailable(player)) return false; + if (this.disableOnSneak && player.isSneaking()) return false; + if (EnchantRegister.VEINMINER != null && item.containsEnchantment(EnchantRegister.VEINMINER)) return false; + if (EnchantRegister.BLAST_MINING != null && item.containsEnchantment(EnchantRegister.BLAST_MINING)) return false; + if (block.hasMetadata(META_BLOCK_TUNNEL)) return false; + if (block.getType().isInteractable() && !INTERACTABLE_BLOCKS.contains(block.getType())) return false; + if (block.getDrops(item).isEmpty()) return false; + if (!this.checkTriggerChance(level)) return false; + if (!this.takeCostItem(player)) return false; + + BlockFace dir = LocationUtil.getDirection(player); + boolean isY = dir != null && block.getRelative(dir.getOppositeFace()).isEmpty(); + boolean isZ = dir == BlockFace.EAST || dir == BlockFace.WEST; + + // Mine + shape if Tunnel I, 3x3 if Tunnel II + int blocksBroken = 1; + if (level == 1) blocksBroken = 2; + else if (level == 2) blocksBroken = 5; + else if (level == 3) blocksBroken = 9; + + HookNCP.exemptBlocks(player); + + for (int i = 0; i < blocksBroken; i++) { + if (item.getType().isAir()) break; + + int xAdd = MINING_COORD_OFFSETS[i][0]; + int zAdd = MINING_COORD_OFFSETS[i][1]; + + Block blockAdd; + if (isY) { + blockAdd = block.getLocation().clone().add(isZ ? 0 : xAdd, zAdd, isZ ? xAdd : 0).getBlock(); + } + else { + blockAdd = block.getLocation().clone().add(xAdd, 0, zAdd).getBlock(); + } + + // Skip blocks that should not be mined + if (blockAdd.equals(block)) continue; + if (blockAdd.getDrops(item).isEmpty()) continue; + if (blockAdd.isLiquid()) continue; + + Material addType = blockAdd.getType(); + + // Some extra block checks. + if (addType.isInteractable() && !INTERACTABLE_BLOCKS.contains(addType)) continue; + if (addType == Material.BEDROCK || addType == Material.END_PORTAL || addType == Material.END_PORTAL_FRAME) continue; + if (addType == Material.OBSIDIAN && addType != block.getType()) continue; + + // Play block break particles before it's broken. + EffectUtil.playEffect(LocationUtil.getCenter(blockAdd.getLocation()), Particle.BLOCK_CRACK.name(), blockAdd.getType().name(), 0.2, 0.2, 0.2, 0.1, 20); + + // Add metadata to prevent enchantment triggering in a loop. + blockAdd.setMetadata(META_BLOCK_TUNNEL, new FixedMetadataValue(plugin, true)); + plugin.getNMS().breakBlock(player, blockAdd); + blockAdd.removeMetadata(META_BLOCK_TUNNEL, plugin); + } + + HookNCP.unexemptBlocks(player); + return true; + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/tool/EnchantVeinminer.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/tool/EnchantVeinminer.java new file mode 100644 index 0000000..bff7eb7 --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/tool/EnchantVeinminer.java @@ -0,0 +1,138 @@ +package su.nightexpress.excellentenchants.manager.enchants.tool; + +import org.bukkit.Material; +import org.bukkit.Particle; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.enchantments.EnchantmentTarget; +import org.bukkit.entity.Player; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.metadata.FixedMetadataValue; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +import su.nexmedia.engine.manager.leveling.Scaler; +import su.nexmedia.engine.utils.EffectUtil; +import su.nexmedia.engine.utils.LocationUtil; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.EnchantPriority; +import su.nightexpress.excellentenchants.api.enchantment.IEnchantChanceTemplate; +import su.nightexpress.excellentenchants.api.enchantment.type.BlockBreakEnchant; +import su.nightexpress.excellentenchants.hook.HookNCP; +import su.nightexpress.excellentenchants.manager.EnchantRegister; +import su.nightexpress.excellentenchants.manager.object.EnchantScaler; +import su.nightexpress.excellentenchants.manager.type.FitItemType; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; +import java.util.function.UnaryOperator; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class EnchantVeinminer extends IEnchantChanceTemplate implements BlockBreakEnchant { + + private final Scaler blocksLimit; + private final Set blocksAffected; + + public static final String ID = "veinminer"; + + private static final BlockFace[] AREA = {BlockFace.UP, BlockFace.DOWN, BlockFace.EAST, BlockFace.WEST, BlockFace.SOUTH, BlockFace.NORTH}; + private static final String META_BLOCK_VEINED = ID + "_block_veined"; + private static final String PLACEHOLDER_BLOCK_LIMIT = "%enchantment_block_limit%"; + + public EnchantVeinminer(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg) { + super(plugin, cfg, EnchantPriority.HIGH); + + this.blocksLimit = new EnchantScaler(this, "Settings.Blocks.Max_At_Once"); + this.blocksAffected = cfg.getStringSet("Settings.Blocks.Affected").stream() + .map(type -> Material.getMaterial(type.toUpperCase())).filter(Objects::nonNull).collect(Collectors.toSet()); + } + + @Override + protected void addConflicts() { + super.addConflicts(); + this.addConflict(EnchantRegister.TUNNEL); + this.addConflict(EnchantRegister.BLAST_MINING); + } + + @NotNull + public Set getBlocksAffected() { + return this.blocksAffected; + } + + public int getBlocksLimit(int level) { + return (int) this.blocksLimit.getValue(level); + } + + @Override + @NotNull + public UnaryOperator replacePlaceholders(int level) { + return str -> super.replacePlaceholders(level).apply(str + .replace(PLACEHOLDER_BLOCK_LIMIT, String.valueOf(this.getBlocksLimit(level))) + ); + } + + @Override + @NotNull + public FitItemType[] getFitItemTypes() { + return new FitItemType[]{FitItemType.PICKAXE}; + } + + @NotNull + @Override + public EnchantmentTarget getItemTarget() { + return EnchantmentTarget.TOOL; + } + + @NotNull + private Set getNearby(@NotNull Block block) { + return Stream.of(AREA).map(block::getRelative) + .filter(blockAdded -> blockAdded.getType() == block.getType()).collect(Collectors.toSet()); + } + + private void vein(@NotNull Player player, @NotNull Block source, int level) { + Set ores = new HashSet<>(); + Set prepare = new HashSet<>(this.getNearby(source)); + + int limit = Math.min(this.getBlocksLimit(level), 30); + if (limit < 0) return; + + while (ores.addAll(prepare) && ores.size() < limit) { + Set nearby = new HashSet<>(); + prepare.forEach(prepared -> nearby.addAll(this.getNearby(prepared))); + prepare.clear(); + prepare.addAll(nearby); + } + ores.remove(source); + ores.forEach(ore -> { + // Play block break particles before the block broken. + EffectUtil.playEffect(LocationUtil.getCenter(ore.getLocation()), Particle.BLOCK_CRACK.name(), ore.getType().name(), 0.2, 0.2, 0.2, 0.1, 20); + + ore.setMetadata(META_BLOCK_VEINED, new FixedMetadataValue(plugin, true)); + plugin.getNMS().breakBlock(player, ore); + ore.removeMetadata(META_BLOCK_VEINED, plugin); + }); + } + + @Override + public boolean use(@NotNull BlockBreakEvent e, @NotNull Player player, @NotNull ItemStack tool, int level) { + if (!this.isEnchantmentAvailable(player)) return false; + if (EnchantRegister.TUNNEL != null && tool.containsEnchantment(EnchantRegister.TUNNEL)) return false; + if (EnchantRegister.BLAST_MINING != null && tool.containsEnchantment(EnchantRegister.BLAST_MINING)) return false; + + Block block = e.getBlock(); + if (block.hasMetadata(META_BLOCK_VEINED)) return false; + if (block.getDrops(tool).isEmpty()) return false; + + if (!this.getBlocksAffected().contains(block.getType())) return false; + if (!this.checkTriggerChance(level)) return false; + if (!this.takeCostItem(player)) return false; + + HookNCP.exemptBlocks(player); + this.vein(player, block, level); + HookNCP.unexemptBlocks(player); + + return true; + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantBaneOfNetherspawn.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantBaneOfNetherspawn.java new file mode 100644 index 0000000..13a6136 --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantBaneOfNetherspawn.java @@ -0,0 +1,106 @@ +package su.nightexpress.excellentenchants.manager.enchants.weapon; + +import com.google.common.collect.Sets; +import org.bukkit.Particle; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.enchantments.EnchantmentTarget; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.LivingEntity; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +import su.nexmedia.engine.manager.leveling.Scaler; +import su.nexmedia.engine.utils.EffectUtil; +import su.nexmedia.engine.utils.NumberUtil; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.EnchantPriority; +import su.nightexpress.excellentenchants.api.enchantment.IEnchantChanceTemplate; +import su.nightexpress.excellentenchants.api.enchantment.type.CombatEnchant; +import su.nightexpress.excellentenchants.manager.EnchantRegister; +import su.nightexpress.excellentenchants.manager.object.EnchantScaler; + +import java.util.Set; +import java.util.function.UnaryOperator; + +public class EnchantBaneOfNetherspawn extends IEnchantChanceTemplate implements CombatEnchant { + + private final String particleName; + private final String particleData; + private final boolean damageModifier; + private final Scaler damageFormula; + private final Set entityTypes; + + public static final String ID = "bane_of_netherspawn"; + + private static final String PLACEHOLDER_DAMAGE = "%enchantment_damage%"; + + public EnchantBaneOfNetherspawn(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg) { + super(plugin, cfg, EnchantPriority.MEDIUM); + + this.particleName = cfg.getString("Settings.Particle.Name", ""); + this.particleData = cfg.getString("Settings.Particle.Data", ""); + this.damageModifier = cfg.getBoolean("Settings.Damage.As_Modifier"); + this.damageFormula = new EnchantScaler(this, "Settings.Damage.Formula"); + + this.entityTypes = Sets.newHashSet(EntityType.BLAZE, EntityType.MAGMA_CUBE, + EntityType.WITHER_SKELETON, EntityType.GHAST, EntityType.WITHER); + + this.entityTypes.add(EntityType.PIGLIN); + this.entityTypes.add(EntityType.PIGLIN_BRUTE); + this.entityTypes.add(EntityType.ZOGLIN); + this.entityTypes.add(EntityType.HOGLIN); + this.entityTypes.add(EntityType.STRIDER); + this.entityTypes.add(EntityType.ZOMBIFIED_PIGLIN); + } + + public double getDamageModifier(int level) { + return this.damageFormula.getValue(level); + } + + @Override + protected void updateConfig() { + super.updateConfig(); + + cfg.remove("Settings.Particle_Effect"); + cfg.addMissing("Settings.Particle.Name", Particle.SMOKE_NORMAL.name()); + cfg.addMissing("Settings.Particle.Data", ""); + } + + @Override + protected void addConflicts() { + super.addConflicts(); + this.addConflict(EnchantRegister.VILLAGE_DEFENDER); + this.addConflict(Enchantment.DAMAGE_ARTHROPODS); + this.addConflict(Enchantment.DAMAGE_UNDEAD); + this.addConflict(Enchantment.DAMAGE_ALL); + } + + @Override + @NotNull + public UnaryOperator replacePlaceholders(int level) { + return str -> super.replacePlaceholders(level).apply(str + .replace(PLACEHOLDER_DAMAGE, NumberUtil.format(this.getDamageModifier(level))) + ); + } + + @Override + @NotNull + public EnchantmentTarget getItemTarget() { + return EnchantmentTarget.WEAPON; + } + + @Override + public boolean use(@NotNull EntityDamageByEntityEvent e, @NotNull LivingEntity damager, @NotNull LivingEntity victim, @NotNull ItemStack weapon, int level) { + if (!this.isEnchantmentAvailable(damager)) return false; + if (!this.entityTypes.contains(victim.getType())) return false; + if (!this.checkTriggerChance(level)) return false; + if (!this.takeCostItem(damager)) return false; + + double damageEvent = e.getDamage(); + double damageAdd = this.getDamageModifier(level); + e.setDamage(this.damageModifier ? damageEvent * damageAdd : damageEvent + damageAdd); + EffectUtil.playEffect(victim.getEyeLocation(), this.particleName, this.particleData, 0.25, 0.25, 0.25, 0.1f, 30); + return true; + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantBlindness.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantBlindness.java new file mode 100644 index 0000000..9b9bd7e --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantBlindness.java @@ -0,0 +1,17 @@ +package su.nightexpress.excellentenchants.manager.enchants.weapon; + +import org.bukkit.potion.PotionEffectType; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.EnchantPriority; +import su.nightexpress.excellentenchants.api.enchantment.IEnchantCombatPotionTemplate; + +public class EnchantBlindness extends IEnchantCombatPotionTemplate { + + public static final String ID = "blindness"; + + public EnchantBlindness(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg) { + super(plugin, cfg, EnchantPriority.MEDIUM, PotionEffectType.BLINDNESS); + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantConfusion.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantConfusion.java new file mode 100644 index 0000000..3eee512 --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantConfusion.java @@ -0,0 +1,17 @@ +package su.nightexpress.excellentenchants.manager.enchants.weapon; + +import org.bukkit.potion.PotionEffectType; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.EnchantPriority; +import su.nightexpress.excellentenchants.api.enchantment.IEnchantCombatPotionTemplate; + +public class EnchantConfusion extends IEnchantCombatPotionTemplate { + + public static final String ID = "confusion"; + + public EnchantConfusion(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg) { + super(plugin, cfg, EnchantPriority.MEDIUM, PotionEffectType.CONFUSION); + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantCure.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantCure.java new file mode 100644 index 0000000..a646ece --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantCure.java @@ -0,0 +1,79 @@ +package su.nightexpress.excellentenchants.manager.enchants.weapon; + +import com.google.common.collect.Sets; +import org.bukkit.Particle; +import org.bukkit.Sound; +import org.bukkit.enchantments.EnchantmentTarget; +import org.bukkit.entity.*; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +import su.nexmedia.engine.utils.EffectUtil; +import su.nexmedia.engine.utils.MessageUtil; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.EnchantPriority; +import su.nightexpress.excellentenchants.api.enchantment.IEnchantChanceTemplate; +import su.nightexpress.excellentenchants.api.enchantment.type.CombatEnchant; + +import java.util.Set; + +public class EnchantCure extends IEnchantChanceTemplate implements CombatEnchant { + + private final Sound sound; + private final String particleName; + private final String particleData; + + public static final String ID = "cure"; + + private static final Set MOBS_TO_CURE = Sets.newHashSet(EntityType.ZOMBIFIED_PIGLIN, EntityType.ZOMBIE_VILLAGER); + + public EnchantCure(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg) { + super(plugin, cfg, EnchantPriority.MEDIUM); + + this.sound = cfg.getEnum("Settings.Sound", Sound.class); + this.particleName = cfg.getString("Settings.Particle.Name", Particle.CLOUD.name()); + this.particleData = cfg.getString("Settings.Particle.Data", ""); + } + + @Override + protected void updateConfig() { + super.updateConfig(); + + cfg.remove("Settings.Particle_Effect"); + cfg.addMissing("Settings.Particle.Name", Particle.CLOUD.name()); + cfg.addMissing("Settings.Particle.Data", ""); + } + + @Override + @NotNull + public EnchantmentTarget getItemTarget() { + return EnchantmentTarget.WEAPON; + } + + @Override + public boolean use(@NotNull EntityDamageByEntityEvent e, @NotNull LivingEntity damager, @NotNull LivingEntity victim, @NotNull ItemStack weapon, int level) { + if (!this.isEnchantmentAvailable(damager)) return false; + if (!MOBS_TO_CURE.contains(victim.getType())) return false; + if (!this.checkTriggerChance(level)) return false; + if (!this.takeCostItem(damager)) return false; + + e.setCancelled(true); + + EffectUtil.playEffect(victim.getLocation(), this.particleName, this.particleData, 0.25, 0.25, 0.25, 0.1f, 20); + if (this.sound != null) MessageUtil.sound(victim.getLocation(), this.sound); + + if (victim instanceof PigZombie pigZombie) { + victim.getWorld().spawn(victim.getLocation(), Piglin.class); + } + else if (victim instanceof ZombieVillager zombieVillager) { + Villager.Profession profession = zombieVillager.getVillagerProfession(); + Villager villager = victim.getWorld().spawn(victim.getLocation(), Villager.class); + if (profession != null) { + villager.setProfession(profession); + } + } + victim.remove(); + return true; + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantCutter.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantCutter.java new file mode 100644 index 0000000..c7d6298 --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantCutter.java @@ -0,0 +1,102 @@ +package su.nightexpress.excellentenchants.manager.enchants.weapon; + +import org.bukkit.Particle; +import org.bukkit.Sound; +import org.bukkit.enchantments.EnchantmentTarget; +import org.bukkit.entity.Item; +import org.bukkit.entity.LivingEntity; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.inventory.EntityEquipment; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.Damageable; +import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +import su.nexmedia.engine.manager.leveling.Scaler; +import su.nexmedia.engine.utils.EffectUtil; +import su.nexmedia.engine.utils.MessageUtil; +import su.nexmedia.engine.utils.NumberUtil; +import su.nexmedia.engine.utils.random.Rnd; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.EnchantPriority; +import su.nightexpress.excellentenchants.api.enchantment.IEnchantChanceTemplate; +import su.nightexpress.excellentenchants.api.enchantment.type.CombatEnchant; +import su.nightexpress.excellentenchants.manager.object.EnchantScaler; + +import java.util.function.UnaryOperator; + +public class EnchantCutter extends IEnchantChanceTemplate implements CombatEnchant { + + protected Scaler durabilityReduction; + protected Sound sound; + + public static final String ID = "cutter"; + + private static final String PLACEHOLDER_DURABILITY_DAMAGE = "%enchantment_durability_damage%"; + + public EnchantCutter(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg) { + super(plugin, cfg, EnchantPriority.LOWEST); + this.durabilityReduction = new EnchantScaler(this, "Settings.Item.Durability_Reduction"); + this.sound = cfg.getEnum("Settings.Item.Sound", Sound.class); + } + + public final double getDurabilityReduction(int level) { + return this.durabilityReduction.getValue(level); + } + + @Override + protected void updateConfig() { + super.updateConfig(); + + cfg.addMissing("Settings.Item.Sound", Sound.ENTITY_ITEM_BREAK.name()); + } + + @Override + @NotNull + public UnaryOperator replacePlaceholders(int level) { + return str -> super.replacePlaceholders(level).apply(str + .replace(PLACEHOLDER_DURABILITY_DAMAGE, NumberUtil.format(this.getDurabilityReduction(level) * 100D)) + ); + } + + @Override + @NotNull + public EnchantmentTarget getItemTarget() { + return EnchantmentTarget.WEAPON; + } + + @Override + public boolean use(@NotNull EntityDamageByEntityEvent e, @NotNull LivingEntity damager, @NotNull LivingEntity victim, @NotNull ItemStack weapon, int level) { + if (!this.isEnchantmentAvailable(damager)) return false; + + EntityEquipment equipment = victim.getEquipment(); + if (equipment == null) return false; + + ItemStack[] armor = equipment.getArmorContents(); + if (armor.length == 0) return false; + + int get = Rnd.get(armor.length); + ItemStack itemCut = armor[get]; + + if (itemCut == null || itemCut.getType().isAir() || itemCut.getType().getMaxDurability() == 0) return false; + + ItemMeta meta = itemCut.getItemMeta(); + if (!(meta instanceof Damageable damageable)) return false; + if (!this.checkTriggerChance(level)) return false; + if (!this.takeCostItem(damager)) return false; + + damageable.setDamage((int) (itemCut.getType().getMaxDurability() * this.getDurabilityReduction(level))); + itemCut.setItemMeta(damageable); + + armor[get] = null; + equipment.setArmorContents(armor); + + Item drop = victim.getWorld().dropItemNaturally(victim.getLocation(), itemCut); + drop.setPickupDelay(50); + drop.getVelocity().multiply(3D); + + EffectUtil.playEffect(victim.getEyeLocation(), Particle.ITEM_CRACK.name(), itemCut.getType().name(), 0.2f, 0.15f, 0.2f, 0.15f, 40); + if (this.sound != null) MessageUtil.sound(victim.getLocation(), this.sound.name()); + return true; + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantDecapitator.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantDecapitator.java new file mode 100644 index 0000000..d12c449 --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantDecapitator.java @@ -0,0 +1,122 @@ +package su.nightexpress.excellentenchants.manager.enchants.weapon; + + +import org.bukkit.Material; +import org.bukkit.Particle; +import org.bukkit.enchantments.EnchantmentTarget; +import org.bukkit.entity.*; +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.JYML; +import su.nexmedia.engine.utils.EffectUtil; +import su.nexmedia.engine.utils.ItemUtil; +import su.nexmedia.engine.utils.StringUtil; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.EnchantPriority; +import su.nightexpress.excellentenchants.api.enchantment.IEnchantChanceTemplate; +import su.nightexpress.excellentenchants.api.enchantment.type.DeathEnchant; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +public class EnchantDecapitator extends IEnchantChanceTemplate implements DeathEnchant { + + private final String particleName; + private final String particleData; + private final Set ignoredEntityTypes; + private final String headName; + private final Map headTextures; + + public static final String ID = "decapitator"; + + public EnchantDecapitator(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg) { + super(plugin, cfg, EnchantPriority.MEDIUM); + + this.particleName = cfg.getString("Settings.Particle.Name", Particle.BLOCK_CRACK.name()); + this.particleData = cfg.getString("Settings.Particle.Data", Material.REDSTONE_BLOCK.name()); + this.ignoredEntityTypes = cfg.getStringSet("Settings.Ignored_Entity_Types").stream().map(String::toUpperCase).collect(Collectors.toSet()); + this.headName = StringUtil.color(cfg.getString("Settings.Head_Item.Name", "&c%entity%'s Head")); + this.headTextures = new HashMap<>(); + for (String sType : cfg.getSection("Settings.Head_Item.Textures")) { + String texture = cfg.getString("Settings.Head_Item.Textures." + sType); + this.headTextures.put(sType.toUpperCase(), texture); + } + } + + @Override + protected void updateConfig() { + super.updateConfig(); + + this.cfg.remove("Settings.Particle_Effect"); + this.cfg.addMissing("Settings.Particle.Name", Particle.BLOCK_CRACK.name()); + this.cfg.addMissing("Settings.Particle.Data", Material.REDSTONE_BLOCK.name()); + this.cfg.addMissing("Settings.Ignored_Entity_Types", Arrays.asList("ENDER_DRAGON", "WITHER_SKELETON", "WITHER")); + } + + @Override + @NotNull + public EnchantmentTarget getItemTarget() { + return EnchantmentTarget.WEAPON; + } + + @Override + public boolean use(@NotNull EntityDeathEvent e, @NotNull LivingEntity victim, int level) { + if (!this.isEnchantmentAvailable(victim)) return false; + + EntityType entityType = victim.getType(); + if (this.ignoredEntityTypes.contains(entityType.name())) return false; + if (!this.checkTriggerChance(level)) return false; + + Player killer = victim.getKiller(); + if (killer == null) return false; + if (!this.takeCostItem(killer)) return false; + + ItemStack item; + if (entityType == EntityType.WITHER_SKELETON || entityType == EntityType.WITHER) { + item = new ItemStack(Material.WITHER_SKELETON_SKULL); + } + else if (entityType == EntityType.ZOMBIE || entityType == EntityType.GIANT) { + item = new ItemStack(Material.ZOMBIE_HEAD); + } + else if (entityType == EntityType.SKELETON) { + item = new ItemStack(Material.SKELETON_SKULL); + } + else if (entityType == EntityType.CREEPER) { + item = new ItemStack(Material.CREEPER_HEAD); + } + else if (entityType == EntityType.ENDER_DRAGON) { + item = new ItemStack(Material.DRAGON_HEAD); + } + else { + item = new ItemStack(Material.PLAYER_HEAD); + SkullMeta meta = (SkullMeta) item.getItemMeta(); + if (meta == null) return false; + + String entityName; + if (victim instanceof Player player) { + entityName = this.headName.replace("%entity%", victim.getName()); + meta.setOwningPlayer(player); + } + else { + String texture = this.headTextures.get(victim.getType().name()); + if (texture == null) return false; + + entityName = this.headName.replace("%entity%", plugin.lang().getEnum(victim.getType())); + ItemUtil.addSkullTexture(item, texture, ID + victim.getType().name()); + meta = (SkullMeta) item.getItemMeta(); + } + + meta.setDisplayName(entityName); + item.setItemMeta(meta); + } + + victim.getWorld().dropItemNaturally(victim.getLocation(), item); + EffectUtil.playEffect(victim.getEyeLocation(), this.particleName, this.particleData, 0.2f, 0.15f, 0.2f, 0.15f, 40); + return true; + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantDoubleStrike.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantDoubleStrike.java new file mode 100644 index 0000000..e967956 --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantDoubleStrike.java @@ -0,0 +1,60 @@ +package su.nightexpress.excellentenchants.manager.enchants.weapon; + +import org.bukkit.Particle; +import org.bukkit.Sound; +import org.bukkit.enchantments.EnchantmentTarget; +import org.bukkit.entity.LivingEntity; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +import su.nexmedia.engine.utils.EffectUtil; +import su.nexmedia.engine.utils.MessageUtil; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.EnchantPriority; +import su.nightexpress.excellentenchants.api.enchantment.IEnchantChanceTemplate; +import su.nightexpress.excellentenchants.api.enchantment.type.CombatEnchant; + +public class EnchantDoubleStrike extends IEnchantChanceTemplate implements CombatEnchant { + + private final String particleName; + private final String particleData; + private final Sound sound; + + public static final String ID = "double_strike"; + + public EnchantDoubleStrike(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg) { + super(plugin, cfg, EnchantPriority.LOW); + + this.particleName = cfg.getString("Settings.Particle.Name", Particle.EXPLOSION_NORMAL.name()); + this.particleData = cfg.getString("Settings.Particle.Data", ""); + this.sound = cfg.getEnum("Settings.Sound", Sound.class); + } + + @Override + protected void updateConfig() { + super.updateConfig(); + + cfg.remove("Settings.Particle_Effect"); + cfg.addMissing("Settings.Particle.Name", Particle.EXPLOSION_NORMAL.name()); + cfg.addMissing("Settings.Particle.Data", ""); + } + + @Override + @NotNull + public EnchantmentTarget getItemTarget() { + return EnchantmentTarget.WEAPON; + } + + @Override + public boolean use(@NotNull EntityDamageByEntityEvent e, @NotNull LivingEntity damager, @NotNull LivingEntity victim, @NotNull ItemStack weapon, int level) { + if (!this.isEnchantmentAvailable(damager)) return false; + if (!this.checkTriggerChance(level)) return false; + if (!this.takeCostItem(damager)) return false; + + e.setDamage(e.getDamage() * 2D); + EffectUtil.playEffect(victim.getEyeLocation(), this.particleName, this.particleData, 0.2f, 0.15f, 0.2f, 0.15f, 20); + if (this.sound != null) MessageUtil.sound(victim.getLocation(), this.sound); + return true; + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantExhaust.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantExhaust.java new file mode 100644 index 0000000..41501f9 --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantExhaust.java @@ -0,0 +1,17 @@ +package su.nightexpress.excellentenchants.manager.enchants.weapon; + +import org.bukkit.potion.PotionEffectType; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.EnchantPriority; +import su.nightexpress.excellentenchants.api.enchantment.IEnchantCombatPotionTemplate; + +public class EnchantExhaust extends IEnchantCombatPotionTemplate { + + public static final String ID = "exhaust"; + + public EnchantExhaust(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg) { + super(plugin, cfg, EnchantPriority.MEDIUM, PotionEffectType.HUNGER); + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantExpHunter.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantExpHunter.java new file mode 100644 index 0000000..5c7eeba --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantExpHunter.java @@ -0,0 +1,70 @@ +package su.nightexpress.excellentenchants.manager.enchants.weapon; + +import org.bukkit.enchantments.EnchantmentTarget; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.event.entity.EntityDeathEvent; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +import su.nexmedia.engine.manager.leveling.Scaler; +import su.nexmedia.engine.utils.NumberUtil; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.EnchantPriority; +import su.nightexpress.excellentenchants.api.enchantment.IEnchantChanceTemplate; +import su.nightexpress.excellentenchants.api.enchantment.type.DeathEnchant; +import su.nightexpress.excellentenchants.manager.object.EnchantScaler; + +import java.util.function.UnaryOperator; + +public class EnchantExpHunter extends IEnchantChanceTemplate implements DeathEnchant { + + private final Scaler expModifier; + + public static final String ID = "exp_hunter"; + public static final String PLACEHOLDER_EXP_MODIFIER = "%enchantment_exp_modifier%"; + + public EnchantExpHunter(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg) { + super(plugin, cfg, EnchantPriority.MEDIUM); + this.expModifier = new EnchantScaler(this, "Settings.Exp_Modifier"); + } + + public final double getExpModifier(int level) { + return this.expModifier.getValue(level); + } + + @Override + protected void updateConfig() { + super.updateConfig(); + } + + @Override + @NotNull + public UnaryOperator replacePlaceholders(int level) { + return str -> super.replacePlaceholders(level).apply(str + .replace(PLACEHOLDER_EXP_MODIFIER, NumberUtil.format(this.getExpModifier(level) * 100D - 100D)) + ); + } + + @Override + @NotNull + public EnchantmentTarget getItemTarget() { + return EnchantmentTarget.WEAPON; + } + + @Override + public boolean use(@NotNull EntityDeathEvent e, @NotNull LivingEntity dead, int level) { + if (!this.isEnchantmentAvailable(dead)) return false; + + Player killer = dead.getKiller(); + if (killer == null) return false; + + if (!this.checkTriggerChance(level)) return false; + if (!this.takeCostItem(killer)) return false; + + double expModifier = this.getExpModifier(level); + double expFinal = Math.ceil((double) e.getDroppedExp() * expModifier); + + e.setDroppedExp((int) expFinal); + return true; + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantIceAspect.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantIceAspect.java new file mode 100644 index 0000000..9a1eb76 --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantIceAspect.java @@ -0,0 +1,31 @@ +package su.nightexpress.excellentenchants.manager.enchants.weapon; + +import org.bukkit.entity.LivingEntity; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.potion.PotionEffectType; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.Version; +import su.nexmedia.engine.api.config.JYML; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.EnchantPriority; +import su.nightexpress.excellentenchants.api.enchantment.IEnchantCombatPotionTemplate; + +public class EnchantIceAspect extends IEnchantCombatPotionTemplate { + + public static final String ID = "ice_aspect"; + + public EnchantIceAspect(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg) { + super(plugin, cfg, EnchantPriority.MEDIUM, PotionEffectType.SLOW); + } + + @Override + public boolean use(@NotNull EntityDamageByEntityEvent e, @NotNull LivingEntity damager, @NotNull LivingEntity victim, @NotNull ItemStack weapon, int level) { + if (!super.use(e, damager, victim, weapon, level)) return false; + + if (Version.CURRENT.isHigher(Version.V1_16_R3)) { + victim.setFreezeTicks(victim.getMaxFreezeTicks()); + } + return true; + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantInfernus.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantInfernus.java new file mode 100644 index 0000000..08b65ea --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantInfernus.java @@ -0,0 +1,89 @@ +package su.nightexpress.excellentenchants.manager.enchants.weapon; + +import org.bukkit.enchantments.EnchantmentTarget; +import org.bukkit.entity.Entity; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Trident; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.entity.ProjectileLaunchEvent; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +import su.nexmedia.engine.manager.leveling.Scaler; +import su.nexmedia.engine.utils.NumberUtil; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.EnchantPriority; +import su.nightexpress.excellentenchants.api.enchantment.IEnchantChanceTemplate; +import su.nightexpress.excellentenchants.manager.object.EnchantScaler; + +import java.util.function.UnaryOperator; + +public class EnchantInfernus extends IEnchantChanceTemplate { + + private final Scaler fireTicks; + + public static final String ID = "infernus"; + public static final String PLACEHOLDER_FIRE_DURATION = "%enchantment_fire_duration%"; + + public EnchantInfernus(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg) { + super(plugin, cfg, EnchantPriority.MEDIUM); + + this.fireTicks = new EnchantScaler(this, "Settings.Fire_Ticks"); + } + + public int getFireTicks(int level) { + return (int) this.fireTicks.getValue(level); + } + + @Override + protected void updateConfig() { + super.updateConfig(); + } + + @Override + @NotNull + public UnaryOperator replacePlaceholders(int level) { + return str -> super.replacePlaceholders(level).apply(str + .replace(PLACEHOLDER_FIRE_DURATION, NumberUtil.format((double) this.getFireTicks(level) / 20D)) + ); + } + + @Override + @NotNull + public EnchantmentTarget getItemTarget() { + return EnchantmentTarget.TRIDENT; + } + + @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) + public void onInfernusTridentLaunch(ProjectileLaunchEvent e) { + Entity entity = e.getEntity(); + if (!(entity instanceof Trident trident)) return; + if (!(trident.getShooter() instanceof LivingEntity shooter)) return; + if (!this.isEnchantmentAvailable(shooter)) return; + + ItemStack item = trident.getItem(); + + int level = item.getEnchantmentLevel(this); + if (level <= 0) return; + + if (!this.checkTriggerChance(level)) return; + if (!this.takeCostItem(shooter)) return; + trident.setFireTicks(Integer.MAX_VALUE); + } + + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + public void onInfernusDamageApply(EntityDamageByEntityEvent e) { + Entity entity = e.getDamager(); + if (!(entity instanceof Trident trident)) return; + + ItemStack item = trident.getItem(); + + int level = item.getEnchantmentLevel(this); + if (level <= 0 || trident.getFireTicks() <= 0) return; + + int ticks = this.getFireTicks(level); + e.getEntity().setFireTicks(ticks); + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantNimble.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantNimble.java new file mode 100644 index 0000000..85b13f9 --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantNimble.java @@ -0,0 +1,43 @@ +package su.nightexpress.excellentenchants.manager.enchants.weapon; + +import org.bukkit.enchantments.EnchantmentTarget; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.event.entity.EntityDeathEvent; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +import su.nexmedia.engine.utils.PlayerUtil; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.EnchantPriority; +import su.nightexpress.excellentenchants.api.enchantment.IEnchantChanceTemplate; +import su.nightexpress.excellentenchants.api.enchantment.type.DeathEnchant; + +public class EnchantNimble extends IEnchantChanceTemplate implements DeathEnchant { + + public static final String ID = "nimble"; + + public EnchantNimble(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg) { + super(plugin, cfg, EnchantPriority.LOWEST); + } + + @NotNull + @Override + public EnchantmentTarget getItemTarget() { + return EnchantmentTarget.WEAPON; + } + + @Override + public boolean use(@NotNull EntityDeathEvent e, @NotNull LivingEntity dead, int level) { + if (!this.isEnchantmentAvailable(dead)) return false; + + Player player = dead.getKiller(); + if (player == null) return false; + + if (!this.checkTriggerChance(level)) return false; + if (!this.takeCostItem(player)) return false; + + e.getDrops().forEach(item -> PlayerUtil.addItem(player, item)); + e.getDrops().clear(); + return true; + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantParalyze.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantParalyze.java new file mode 100644 index 0000000..e55d476 --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantParalyze.java @@ -0,0 +1,17 @@ +package su.nightexpress.excellentenchants.manager.enchants.weapon; + +import org.bukkit.potion.PotionEffectType; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.EnchantPriority; +import su.nightexpress.excellentenchants.api.enchantment.IEnchantCombatPotionTemplate; + +public class EnchantParalyze extends IEnchantCombatPotionTemplate { + + public static final String ID = "paralyze"; + + public EnchantParalyze(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg) { + super(plugin, cfg, EnchantPriority.MEDIUM, PotionEffectType.SLOW_DIGGING); + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantRage.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantRage.java new file mode 100644 index 0000000..9ce4156 --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantRage.java @@ -0,0 +1,25 @@ +package su.nightexpress.excellentenchants.manager.enchants.weapon; + +import org.bukkit.entity.LivingEntity; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.potion.PotionEffectType; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.EnchantPriority; +import su.nightexpress.excellentenchants.api.enchantment.IEnchantCombatPotionTemplate; + +public class EnchantRage extends IEnchantCombatPotionTemplate { + + public static final String ID = "rage"; + + public EnchantRage(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg) { + super(plugin, cfg, EnchantPriority.MEDIUM, PotionEffectType.INCREASE_DAMAGE); + } + + @Override + public boolean use(@NotNull EntityDamageByEntityEvent e, @NotNull LivingEntity damager, @NotNull LivingEntity victim, @NotNull ItemStack weapon, int level) { + return super.use(e, damager, damager, weapon, level); + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantRocket.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantRocket.java new file mode 100644 index 0000000..780e7e5 --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantRocket.java @@ -0,0 +1,61 @@ +package su.nightexpress.excellentenchants.manager.enchants.weapon; + +import org.bukkit.Sound; +import org.bukkit.enchantments.EnchantmentTarget; +import org.bukkit.entity.Firework; +import org.bukkit.entity.LivingEntity; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.FireworkMeta; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +import su.nexmedia.engine.manager.leveling.Scaler; +import su.nexmedia.engine.utils.EntityUtil; +import su.nexmedia.engine.utils.MessageUtil; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.EnchantPriority; +import su.nightexpress.excellentenchants.api.enchantment.IEnchantChanceTemplate; +import su.nightexpress.excellentenchants.api.enchantment.type.CombatEnchant; +import su.nightexpress.excellentenchants.manager.object.EnchantScaler; + +public class EnchantRocket extends IEnchantChanceTemplate implements CombatEnchant { + + private final Scaler fireworkPower; + + public static final String ID = "rocket"; + + public EnchantRocket(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg) { + super(plugin, cfg, EnchantPriority.MEDIUM); + this.fireworkPower = new EnchantScaler(this, "Settings.Firework_Power"); + } + + public final double getFireworkPower(int level) { + return this.fireworkPower.getValue(level); + } + + @Override + @NotNull + public EnchantmentTarget getItemTarget() { + return EnchantmentTarget.WEAPON; + } + + @Override + public boolean use(@NotNull EntityDamageByEntityEvent e, @NotNull LivingEntity damager, @NotNull LivingEntity victim, @NotNull ItemStack weapon, int level) { + if (!this.isEnchantmentAvailable(damager)) return false; + if (!this.checkTriggerChance(level)) return false; + if (!this.takeCostItem(damager)) return false; + + if (victim.isInsideVehicle()) { + victim.leaveVehicle(); + } + + Firework firework = EntityUtil.spawnRandomFirework(victim.getLocation()); + FireworkMeta meta = firework.getFireworkMeta(); + meta.setPower((int) this.getFireworkPower(level)); + firework.setFireworkMeta(meta); + firework.addPassenger(victim); + + MessageUtil.sound(victim.getLocation(), Sound.ENTITY_FIREWORK_ROCKET_LAUNCH); + return true; + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantScavenger.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantScavenger.java new file mode 100644 index 0000000..e28c2e1 --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantScavenger.java @@ -0,0 +1,97 @@ +package su.nightexpress.excellentenchants.manager.enchants.weapon; + +import org.bukkit.Material; +import org.bukkit.enchantments.EnchantmentTarget; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.event.entity.EntityDeathEvent; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +import su.nexmedia.engine.utils.CollectionsUtil; +import su.nexmedia.engine.utils.StringUtil; +import su.nexmedia.engine.utils.random.Rnd; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.EnchantPriority; +import su.nightexpress.excellentenchants.api.enchantment.IEnchantChanceTemplate; +import su.nightexpress.excellentenchants.api.enchantment.type.DeathEnchant; + +import java.util.AbstractMap; +import java.util.HashMap; +import java.util.Map; + +public class EnchantScavenger extends IEnchantChanceTemplate implements DeathEnchant { + + private final Map>> loot; + + public static final String ID = "scavenger"; + + public EnchantScavenger(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg) { + super(plugin, cfg, EnchantPriority.MEDIUM); + this.loot = new HashMap<>(); + + for (String eId : cfg.getSection("Settings.Treasures")) { + EntityType eType = CollectionsUtil.getEnum(eId, EntityType.class); + if (eType == null || !eType.isAlive()) { + plugin.error("[Scavenger] Invalid entity type '" + eId + "' !"); + continue; + } + + Map> items = new HashMap<>(); + for (String sFromArray : cfg.getSection("Settings.Treasures." + eId)) { + Material material = Material.getMaterial(sFromArray.toUpperCase()); + if (material == null) { + plugin.error("[Scavenger] Invalid item material '" + sFromArray + "' !"); + continue; + } + + String path = "Settings.Treasures." + eId + "." + sFromArray + "."; + String[] amountSplit = cfg.getString(path + "Amount", "1:1").split(":"); + int amountMin = StringUtil.getInteger(amountSplit[0], 1); + int amountMax = StringUtil.getInteger(amountSplit[1], 1); + int[] amount = new int[]{amountMin, amountMax}; + + double chance = cfg.getDouble(path + "Chance"); + if (chance <= 0) continue; + + Map.Entry item = new AbstractMap.SimpleEntry<>(amount, chance); + items.put(material, item); + } + this.loot.put(eType, items); + } + } + + @Override + @NotNull + public EnchantmentTarget getItemTarget() { + return EnchantmentTarget.WEAPON; + } + + @Override + public boolean use(@NotNull EntityDeathEvent e, @NotNull LivingEntity dead, int level) { + if (!this.isEnchantmentAvailable(dead)) return false; + + Map> items = this.loot.get(dead.getType()); + if (items == null) return false; + + Player killer = dead.getKiller(); + if (killer == null) return false; + + if (!this.checkTriggerChance(level)) return false; + if (!this.takeCostItem(killer)) return false; + + items.forEach((material, data) -> { + double chance = data.getValue(); + if (Rnd.get(true) > chance) return; + + int amount = Rnd.get(data.getKey()[0], data.getKey()[1]); + if (amount <= 0) return; + + ItemStack item = new ItemStack(material); + e.getDrops().add(item); + }); + + return true; + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantSurprise.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantSurprise.java new file mode 100644 index 0000000..d44ddfb --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantSurprise.java @@ -0,0 +1,28 @@ +package su.nightexpress.excellentenchants.manager.enchants.weapon; + +import org.bukkit.entity.LivingEntity; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.inventory.ItemStack; +import org.bukkit.potion.PotionEffectType; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +import su.nexmedia.engine.utils.random.Rnd; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.EnchantPriority; +import su.nightexpress.excellentenchants.api.enchantment.IEnchantCombatPotionTemplate; + +public class EnchantSurprise extends IEnchantCombatPotionTemplate { + + public static final String ID = "surprise"; + + public EnchantSurprise(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg) { + super(plugin, cfg, EnchantPriority.MEDIUM, PotionEffectType.BLINDNESS); + + } + + @Override + public boolean use(@NotNull EntityDamageByEntityEvent e, @NotNull LivingEntity damager, @NotNull LivingEntity victim, @NotNull ItemStack weapon, int level) { + this.potionEffectType = Rnd.get(PotionEffectType.values()); + return super.use(e, damager, victim, weapon, level); + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantThrifty.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantThrifty.java new file mode 100644 index 0000000..05df499 --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantThrifty.java @@ -0,0 +1,83 @@ +package su.nightexpress.excellentenchants.manager.enchants.weapon; + +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +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.entity.CreatureSpawnEvent; +import org.bukkit.event.entity.EntityDeathEvent; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +import su.nexmedia.engine.utils.PDCUtil; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.EnchantPriority; +import su.nightexpress.excellentenchants.api.enchantment.IEnchantChanceTemplate; +import su.nightexpress.excellentenchants.api.enchantment.type.DeathEnchant; + +import java.util.Set; +import java.util.stream.Collectors; + +public class EnchantThrifty extends IEnchantChanceTemplate implements DeathEnchant { + + private final Set ignoredEntityTypes; + private final Set ignoredSpawnReasons; + private final NamespacedKey keyEntityIgnored; + + public static final String ID = "thrifty"; + + public EnchantThrifty(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg) { + super(plugin, cfg, EnchantPriority.MEDIUM); + this.keyEntityIgnored = new NamespacedKey(plugin, ID + "_ignored"); + + this.ignoredEntityTypes = cfg.getStringSet("Settings.Ignored_Entity_Types").stream().map(String::toUpperCase).collect(Collectors.toSet()); + this.ignoredSpawnReasons = cfg.getStringSet("Settings.Ignored_Spawn_Reasons").stream().map(String::toUpperCase).collect(Collectors.toSet()); + } + + @Override + protected void updateConfig() { + super.updateConfig(); + } + + @Override + @NotNull + public EnchantmentTarget getItemTarget() { + return EnchantmentTarget.WEAPON; + } + + @Override + public boolean use(@NotNull EntityDeathEvent e, @NotNull LivingEntity dead, int level) { + if (!this.isEnchantmentAvailable(dead)) return false; + + Player killer = dead.getKiller(); + if (killer == null) return false; + + if (this.ignoredEntityTypes.contains(dead.getType().name())) return false; + if (PDCUtil.getBooleanData(dead, this.keyEntityIgnored)) return false; + if (!this.checkTriggerChance(level)) return false; + + Material material = Material.getMaterial(dead.getType().name() + "_SPAWN_EGG"); + if (material == null) { + if (dead.getType() == EntityType.MUSHROOM_COW) { + material = Material.MOOSHROOM_SPAWN_EGG; + } + else return false; + } + if (!this.takeCostItem(killer)) return false; + + ItemStack egg = new ItemStack(material); + e.getDrops().add(egg); + return true; + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onSettingCreatureSpawnReason(CreatureSpawnEvent e) { + if (!this.ignoredSpawnReasons.contains(e.getSpawnReason().name())) return; + + PDCUtil.setData(e.getEntity(), this.keyEntityIgnored, true); + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantThunder.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantThunder.java new file mode 100644 index 0000000..54c0381 --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantThunder.java @@ -0,0 +1,51 @@ +package su.nightexpress.excellentenchants.manager.enchants.weapon; + +import org.bukkit.enchantments.EnchantmentTarget; +import org.bukkit.entity.LivingEntity; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.EnchantPriority; +import su.nightexpress.excellentenchants.api.enchantment.IEnchantChanceTemplate; +import su.nightexpress.excellentenchants.api.enchantment.type.CombatEnchant; + +public class EnchantThunder extends IEnchantChanceTemplate implements CombatEnchant { + + private final boolean inThunderstormOnly; + + public static final String ID = "thunder"; + + public EnchantThunder(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg) { + super(plugin, cfg, EnchantPriority.MEDIUM); + + this.inThunderstormOnly = cfg.getBoolean("Settings.During_Thunderstorm_Only"); + } + + public boolean isInThunderstormOnly() { + return inThunderstormOnly; + } + + @Override + @NotNull + public EnchantmentTarget getItemTarget() { + return EnchantmentTarget.WEAPON; + } + + @Override + public boolean use(@NotNull EntityDamageByEntityEvent e, @NotNull LivingEntity damager, @NotNull LivingEntity victim, @NotNull ItemStack weapon, int level) { + if (!this.isEnchantmentAvailable(damager)) return false; + if (this.isInThunderstormOnly() && !victim.getWorld().isThundering()) return false; + if (victim.getLocation().getBlock().getLightFromSky() != 15) return false; + if (!this.checkTriggerChance(level)) return false; + if (!this.takeCostItem(damager)) return false; + + plugin.getServer().getScheduler().runTask(plugin, () -> { + victim.setNoDamageTicks(0); + victim.getWorld().strikeLightning(victim.getLocation()); + }); + + return true; + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantVampire.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantVampire.java new file mode 100644 index 0000000..c767219 --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantVampire.java @@ -0,0 +1,102 @@ +package su.nightexpress.excellentenchants.manager.enchants.weapon; + +import org.bukkit.Particle; +import org.bukkit.attribute.Attribute; +import org.bukkit.enchantments.EnchantmentTarget; +import org.bukkit.entity.LivingEntity; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.entity.EntityRegainHealthEvent; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +import su.nexmedia.engine.manager.leveling.Scaler; +import su.nexmedia.engine.utils.EffectUtil; +import su.nexmedia.engine.utils.EntityUtil; +import su.nexmedia.engine.utils.NumberUtil; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.EnchantPriority; +import su.nightexpress.excellentenchants.api.enchantment.IEnchantChanceTemplate; +import su.nightexpress.excellentenchants.api.enchantment.type.CombatEnchant; +import su.nightexpress.excellentenchants.manager.object.EnchantScaler; + +import java.util.function.UnaryOperator; + +public class EnchantVampire extends IEnchantChanceTemplate implements CombatEnchant { + + private final String particleName; + private final String particleData; + private final Scaler healAmount; + private final boolean healMultiplier; + + public static final String ID = "vampire"; + public static final String PLACEHOLDER_HEAL_AMOUNT = "%enchantment_heal_amount%"; + + public EnchantVampire(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg) { + super(plugin, cfg, EnchantPriority.LOWEST); + + this.particleName = cfg.getString("Settings.Particle.Name", Particle.HEART.name()); + this.particleData = cfg.getString("Settings.Particle.Data", ""); + this.healAmount = new EnchantScaler(this, "Settings.Heal.Amount"); + this.healMultiplier = cfg.getBoolean("Settings.Heal.As_Multiplier"); + } + + public double getHealAmount(int level) { + return this.healAmount.getValue(level); + } + + public boolean isHealMultiplier() { + return healMultiplier; + } + + @Override + protected void updateConfig() { + super.updateConfig(); + + cfg.remove("Settings.Particle_Effect"); + cfg.remove("Settings.Heal_Of_Damage"); + cfg.addMissing("Settings.Particle.Name", Particle.HEART.name()); + cfg.addMissing("Settings.Particle.Data", ""); + cfg.addMissing("Settings.Heal.Amount", "0.25 * " + PLACEHOLDER_LEVEL); + cfg.addMissing("Settings.Heal.As_Multiplier", false); + } + + @Override + @NotNull + public EnchantmentTarget getItemTarget() { + return EnchantmentTarget.WEAPON; + } + + @Override + @NotNull + public UnaryOperator replacePlaceholders(int level) { + double healAmount = this.getHealAmount(level); + + return str -> super.replacePlaceholders(level).apply(str + .replace(PLACEHOLDER_HEAL_AMOUNT, NumberUtil.format(this.isHealMultiplier() ? healAmount * 100D : healAmount)) + ); + } + + @Override + public boolean use(@NotNull EntityDamageByEntityEvent e, @NotNull LivingEntity damager, @NotNull LivingEntity victim, @NotNull ItemStack weapon, int level) { + if (!this.isEnchantmentAvailable(damager)) return false; + + double healthMax = EntityUtil.getAttribute(damager, Attribute.GENERIC_MAX_HEALTH); + double healthHas = damager.getHealth(); + if (healthHas == healthMax) return false; + + if (!this.checkTriggerChance(level)) return false; + if (!this.takeCostItem(damager)) return false; + + double healAmount = this.getHealAmount(level); + double healFinal = this.isHealMultiplier() ? e.getDamage() * healAmount : healAmount; + + EntityRegainHealthEvent healthEvent = new EntityRegainHealthEvent(damager, healFinal, EntityRegainHealthEvent.RegainReason.CUSTOM); + plugin.getPluginManager().callEvent(healthEvent); + if (healthEvent.isCancelled()) return false; + + damager.setHealth(Math.min(healthMax, healthHas + healthEvent.getAmount())); + + EffectUtil.playEffect(damager.getEyeLocation(), this.particleName, this.particleData, 0.2f, 0.15f, 0.2f, 0.15f, 5); + return true; + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantVenom.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantVenom.java new file mode 100644 index 0000000..c4b3bbe --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantVenom.java @@ -0,0 +1,17 @@ +package su.nightexpress.excellentenchants.manager.enchants.weapon; + +import org.bukkit.potion.PotionEffectType; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.EnchantPriority; +import su.nightexpress.excellentenchants.api.enchantment.IEnchantCombatPotionTemplate; + +public class EnchantVenom extends IEnchantCombatPotionTemplate { + + public static final String ID = "venom"; + + public EnchantVenom(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg) { + super(plugin, cfg, EnchantPriority.MEDIUM, PotionEffectType.POISON); + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantVillageDefender.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantVillageDefender.java new file mode 100644 index 0000000..dda0224 --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantVillageDefender.java @@ -0,0 +1,98 @@ +package su.nightexpress.excellentenchants.manager.enchants.weapon; + +import org.bukkit.Particle; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.enchantments.EnchantmentTarget; +import org.bukkit.entity.Illager; +import org.bukkit.entity.LivingEntity; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +import su.nexmedia.engine.manager.leveling.Scaler; +import su.nexmedia.engine.utils.EffectUtil; +import su.nexmedia.engine.utils.NumberUtil; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.EnchantPriority; +import su.nightexpress.excellentenchants.api.enchantment.IEnchantChanceTemplate; +import su.nightexpress.excellentenchants.api.enchantment.type.CombatEnchant; +import su.nightexpress.excellentenchants.manager.EnchantRegister; +import su.nightexpress.excellentenchants.manager.object.EnchantScaler; + +import java.util.function.UnaryOperator; + +public class EnchantVillageDefender extends IEnchantChanceTemplate implements CombatEnchant { + + private final boolean damageMultiplier; + private final Scaler damageAmount; + private final String particleName; + private final String particleData; + + public static final String ID = "village_defender"; + public static final String PLACEHOLDER_DAMAGE_AMOUNT = "%enchantment_damage_amount%"; + + public EnchantVillageDefender(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg) { + super(plugin, cfg, EnchantPriority.MEDIUM); + + this.damageAmount = new EnchantScaler(this, "Settings.Damage.Formula"); + this.damageMultiplier = cfg.getBoolean("Settings.Damage.As_Modifier"); + this.particleName = cfg.getString("Settings.Particle.Name", Particle.VILLAGER_ANGRY.name()); + this.particleData = cfg.getString("Settings.Particle.Data", ""); + } + + public double getDamageAddict(int level) { + return this.damageAmount.getValue(level); + } + + public boolean isDamageMultiplier() { + return damageMultiplier; + } + + @Override + @NotNull + public UnaryOperator replacePlaceholders(int level) { + return str -> super.replacePlaceholders(level).apply(str + .replace(PLACEHOLDER_DAMAGE_AMOUNT, NumberUtil.format(this.getDamageAddict(level))) + ); + } + + @Override + protected void updateConfig() { + super.updateConfig(); + + cfg.remove("Settings.Particle_Effect"); + cfg.addMissing("Settings.Particle.Name", Particle.VILLAGER_ANGRY.name()); + cfg.addMissing("Settings.Particle.Data", ""); + } + + @Override + protected void addConflicts() { + super.addConflicts(); + this.addConflict(EnchantRegister.BANE_OF_NETHERSPAWN); + this.addConflict(Enchantment.DAMAGE_ARTHROPODS); + this.addConflict(Enchantment.DAMAGE_UNDEAD); + this.addConflict(Enchantment.DAMAGE_ALL); + } + + @Override + @NotNull + public EnchantmentTarget getItemTarget() { + return EnchantmentTarget.WEAPON; + } + + @Override + public boolean use(@NotNull EntityDamageByEntityEvent e, @NotNull LivingEntity damager, @NotNull LivingEntity victim, @NotNull ItemStack weapon, int level) { + if (!this.isEnchantmentAvailable(damager)) return false; + if (!(victim instanceof Illager)) return false; + if (!this.checkTriggerChance(level)) return false; + if (!this.takeCostItem(damager)) return false; + + double damageAdd = this.getDamageAddict(level); + double damageHas = e.getDamage(); + double damageFinal = this.isDamageMultiplier() ? (damageHas * damageAdd) : (damageHas + damageAdd); + + e.setDamage(damageFinal); + EffectUtil.playEffect(victim.getEyeLocation(), this.particleName, this.particleData, 0.15, 0.15, 0.15, 0.13f, 3); + return true; + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantWither.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantWither.java new file mode 100644 index 0000000..0f032ca --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/enchants/weapon/EnchantWither.java @@ -0,0 +1,17 @@ +package su.nightexpress.excellentenchants.manager.enchants.weapon; + +import org.bukkit.potion.PotionEffectType; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.EnchantPriority; +import su.nightexpress.excellentenchants.api.enchantment.IEnchantCombatPotionTemplate; + +public class EnchantWither extends IEnchantCombatPotionTemplate { + + public static final String ID = "wither"; + + public EnchantWither(@NotNull ExcellentEnchants plugin, @NotNull JYML cfg) { + super(plugin, cfg, EnchantPriority.MEDIUM, PotionEffectType.WITHER); + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/listeners/EnchantGenericListener.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/listeners/EnchantGenericListener.java new file mode 100644 index 0000000..17beea8 --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/listeners/EnchantGenericListener.java @@ -0,0 +1,283 @@ +package su.nightexpress.excellentenchants.manager.listeners; + +import org.bukkit.Material; +import org.bukkit.block.Chest; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Item; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Minecart; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.enchantment.EnchantItemEvent; +import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.event.entity.VillagerAcquireTradeEvent; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.inventory.InventoryDragEvent; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.event.inventory.PrepareAnvilEvent; +import org.bukkit.event.player.PlayerFishEvent; +import org.bukkit.event.world.LootGenerateEvent; +import org.bukkit.inventory.*; +import org.bukkit.inventory.meta.EnchantmentStorageMeta; +import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.manager.AbstractListener; +import su.nexmedia.engine.utils.random.Rnd; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.ExcellentEnchant; +import su.nightexpress.excellentenchants.config.Config; +import su.nightexpress.excellentenchants.config.ObtainSettings; +import su.nightexpress.excellentenchants.manager.EnchantManager; +import su.nightexpress.excellentenchants.manager.object.EnchantTier; +import su.nightexpress.excellentenchants.manager.type.ObtainType; + +import java.util.HashMap; +import java.util.Map; + +public class EnchantGenericListener extends AbstractListener { + + public EnchantGenericListener(@NotNull EnchantManager enchantManager) { + super(enchantManager.plugin()); + } + + // --------------------------------------------------------------- + // Handle Anvil + // --------------------------------------------------------------- + @EventHandler(priority = EventPriority.HIGH) + public void onEnchantUpdateAnvil(PrepareAnvilEvent e) { + AnvilInventory inventory = e.getInventory(); + + ItemStack first = inventory.getItem(0); + ItemStack second = inventory.getItem(1); + ItemStack result = e.getResult(); + + // Check if source item is an enchantable single item. + if (first == null || !EnchantManager.isEnchantable(first) || first.getAmount() > 1) return; + + // For repair/rename, only re-add item enchants. + if ((second == null || second.getType().isAir() || !EnchantManager.isEnchantable(second)) && (result != null && result.getType() == first.getType())) { + ItemStack result2 = new ItemStack(result); + EnchantManager.getItemCustomEnchants(first).forEach((hasEnch, hasLevel) -> { + EnchantManager.addEnchant(result2, hasEnch, hasLevel, true); + }); + e.setResult(result2); + return; + } + + // Check if the second item is an enchantable single item. + if (second == null || second.getAmount() > 1 || !EnchantManager.isEnchantable(second)) return; + + // Prevent operation if first item is book while the second one is another item. + if (first.getType() == Material.ENCHANTED_BOOK && second.getType() != first.getType()) return; + + // Fine result item in case if it's nulled somehow. + if (result == null || result.getType() == Material.AIR) { + result = new ItemStack(first); + } + + Map enchAdd = EnchantManager.getItemCustomEnchants(first); + int repairCost = inventory.getRepairCost(); + + // If the second item is an enchanted book or the same item type, then + // we can merge our enchantments. + if (second.getType() == Material.ENCHANTED_BOOK || second.getType() == first.getType()) { + for (Map.Entry en : EnchantManager.getItemCustomEnchants(second).entrySet()) { + enchAdd.merge(en.getKey(), en.getValue(), (oldLvl, newLvl) -> (oldLvl.equals(newLvl)) ? (oldLvl + 1) : (Math.max(oldLvl, newLvl))); + } + } + + // Recalculate operation cost depends on enchantments merge cost. + for (Map.Entry ent : enchAdd.entrySet()) { + ExcellentEnchant enchant = ent.getKey(); + int level = Math.min(enchant.getMaxLevel(), ent.getValue()); + if (EnchantManager.addEnchant(result, enchant, level, false)) { + repairCost += enchant.getAnvilMergeCost(level); + } + } + + if (!first.equals(result)) { + EnchantManager.updateItemLoreEnchants(result); + e.setResult(result); + + // NMS ContainerAnvil will set level cost to 0 right after calling the event + // So we have to change it with a 1 tick delay. + final int repairCost2 = repairCost; + this.plugin.runTask((c) -> inventory.setRepairCost(repairCost2), false); + } + } + + // --------------------------------------------------------------- + // Update enchantment lore after grindstone + // --------------------------------------------------------------- + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onEnchantUpdateGrindstoneClick(InventoryClickEvent e) { + Inventory inventory = e.getInventory(); + if (inventory.getType() != InventoryType.GRINDSTONE) return; + if (e.getRawSlot() == 2) return; + + this.updateGrindstone(inventory); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onEnchantUpdateGrindstoneDrag(InventoryDragEvent e) { + Inventory inventory = e.getInventory(); + if (inventory.getType() != InventoryType.GRINDSTONE) return; + + this.updateGrindstone(inventory); + } + + private void updateGrindstone(@NotNull Inventory inventory) { + this.plugin.getServer().getScheduler().runTask(plugin, () -> { + ItemStack result = inventory.getItem(2); + if (result == null || result.getType().isAir()) return; + + Map curses = new HashMap<>(); + for (int slot = 0; slot < 2; slot++) { + ItemStack source = inventory.getItem(slot); + if (source == null || source.getType().isAir()) continue; + + curses.putAll(EnchantManager.getItemCustomEnchants(source)); + } + curses.entrySet().removeIf(entry -> !entry.getKey().isCursed()); + curses.forEach((excellentEnchant, level) -> { + EnchantManager.addEnchant(result, excellentEnchant, level, true); + }); + EnchantManager.updateItemLoreEnchants(result); + }); + } + + + // --------------------------------------------------------------- + // Handle Enchanting Table + // --------------------------------------------------------------- + @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) + public void onEnchantPopulateEnchantingTable(final EnchantItemEvent e) { + ObtainSettings settings = Config.getObtainSettings(ObtainType.ENCHANTING); + if (settings == null || Rnd.get(true) > settings.getEnchantsCustomGenerationChance()) return; + + ItemStack target = e.getItem(); + boolean enchantAdded = false; + + int enchMax = settings.getEnchantsTotalMax(); + int enchRoll = Rnd.get(settings.getEnchantsCustomMin(), settings.getEnchantsCustomMax()); + + for (int count = 0; (count < enchRoll && e.getEnchantsToAdd().size() < enchMax); count++) { + EnchantTier tier = EnchantManager.getTierByChance(ObtainType.ENCHANTING); + if (tier == null) continue; + + ExcellentEnchant enchant = tier.getEnchant(ObtainType.ENCHANTING, target); + if (enchant == null) continue; + if (e.getEnchantsToAdd().keySet().stream().anyMatch(add -> add.conflictsWith(enchant) || enchant.conflictsWith(add))) + continue; + + int level = enchant.getLevelByEnchantCost(e.getExpLevelCost()); + if (level < 1) continue; + + e.getEnchantsToAdd().put(enchant, level); + enchantAdded = true; + } + + if (enchantAdded) { + plugin.getServer().getScheduler().runTask(plugin, () -> { + ItemStack result = e.getInventory().getItem(0); + if (result == null) return; + + // Fix enchantments for Enchant Books. + // Enchants are not added on book because they do not exists in NMS. + // Server gets enchants from NMS to apply it on Book NBT tags. + ItemMeta meta = result.getItemMeta(); + if (meta instanceof EnchantmentStorageMeta meta2) { + e.getEnchantsToAdd().forEach((en, lvl) -> { + if (!meta2.hasStoredEnchant(en)) { + meta2.addStoredEnchant(en, lvl, true); + } + }); + result.setItemMeta(meta2); + } + + EnchantManager.updateItemLoreEnchants(result); + e.getInventory().setItem(0, result); + }); + } + } + + // --------------------------------------------------------------- + // Adding Enchants to Villagers + // --------------------------------------------------------------- + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onEnchantPopulateVillagerAcquire(VillagerAcquireTradeEvent e) { + MerchantRecipe recipe = e.getRecipe(); + ItemStack result = recipe.getResult(); + + if (!EnchantManager.isEnchantable(result)) return; + if (!EnchantManager.populateEnchantments(result, ObtainType.VILLAGER)) return; + + int uses = recipe.getUses(); + int maxUses = recipe.getMaxUses(); + boolean expReward = recipe.hasExperienceReward(); + int villagerExperience = recipe.getVillagerExperience(); + float priceMultiplier = recipe.getPriceMultiplier(); + + MerchantRecipe recipe2 = new MerchantRecipe(result, uses, maxUses, expReward, villagerExperience, priceMultiplier); + recipe2.setIngredients(recipe.getIngredients()); + e.setRecipe(recipe2); + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onEnchantPopulateLoot(LootGenerateEvent e) { + if (Config.getObtainSettings(ObtainType.LOOT_GENERATION) == null) return; + + Entity entity = e.getEntity(); + InventoryHolder holder = e.getInventoryHolder(); + + if (entity instanceof Minecart || holder instanceof Chest) { + e.getLoot().forEach(item -> { + if (item != null && EnchantManager.isEnchantable(item)) { + EnchantManager.populateEnchantments(item, ObtainType.LOOT_GENERATION); + } + }); + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onEnchantPopulateFishing(PlayerFishEvent e) { + if (Config.getObtainSettings(ObtainType.FISHING) == null) return; + if (e.getState() != PlayerFishEvent.State.CAUGHT_FISH) return; + if (!(e.getCaught() instanceof Item item)) return; + + ItemStack itemStack = item.getItemStack(); + if (EnchantManager.isEnchantable(itemStack)) { + EnchantManager.populateEnchantments(itemStack, ObtainType.FISHING); + } + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onEnchantPopulatePiglinBarter(CreatureSpawnEvent e) { + if (Config.getObtainSettings(ObtainType.MOB_SPAWNING) == null) return; + + LivingEntity entity = e.getEntity(); + EntityEquipment equipment = entity.getEquipment(); + if (equipment == null) return; + + ItemStack[] armor = equipment.getArmorContents(); + for (ItemStack item : armor) { + if (item != null && EnchantManager.isEnchantable(item)) { + EnchantManager.populateEnchantments(item, ObtainType.MOB_SPAWNING); + } + } + + ItemStack itemMain = equipment.getItemInMainHand(); + if (EnchantManager.isEnchantable(itemMain)) { + EnchantManager.populateEnchantments(itemMain, ObtainType.MOB_SPAWNING); + } + + ItemStack itemOff = equipment.getItemInOffHand(); + if (EnchantManager.isEnchantable(itemOff)) { + EnchantManager.populateEnchantments(itemOff, ObtainType.MOB_SPAWNING); + } + + equipment.setArmorContents(armor); + equipment.setItemInMainHand(itemMain); + equipment.setItemInOffHand(itemOff); + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/listeners/EnchantHandlerListener.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/listeners/EnchantHandlerListener.java new file mode 100644 index 0000000..ead48ab --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/listeners/EnchantHandlerListener.java @@ -0,0 +1,195 @@ +package su.nightexpress.excellentenchants.manager.listeners; + +import org.bukkit.GameMode; +import org.bukkit.Material; +import org.bukkit.entity.Entity; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.entity.Projectile; +import org.bukkit.event.Event.Result; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.entity.EntityDamageByEntityEvent; +import org.bukkit.event.entity.EntityDamageEvent.DamageCause; +import org.bukkit.event.entity.EntityDeathEvent; +import org.bukkit.event.entity.EntityShootBowEvent; +import org.bukkit.event.entity.ProjectileHitEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.EntityEquipment; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.manager.AbstractListener; +import su.nexmedia.engine.utils.EntityUtil; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.type.*; +import su.nightexpress.excellentenchants.manager.EnchantManager; + +public class EnchantHandlerListener extends AbstractListener { + + public EnchantHandlerListener(@NotNull EnchantManager enchantManager) { + super(enchantManager.plugin()); + } + + // --------------------------------------------------------------- + // Combat Attacking Enchants + // --------------------------------------------------------------- + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onEnchantCombatMelee(EntityDamageByEntityEvent e) { + if (e.getCause() == DamageCause.THORNS) return; + if (!(e.getEntity() instanceof LivingEntity victim)) return; + if (!(e.getDamager() instanceof LivingEntity damager)) return; + + EntityEquipment equipment = damager.getEquipment(); + if (equipment == null) return; + + ItemStack weapon = equipment.getItemInMainHand(); + if (weapon.getType().isAir() || weapon.getType() == Material.ENCHANTED_BOOK) return; + + EnchantManager.getItemCustomEnchants(weapon, CombatEnchant.class).forEach((combatEnchant, level) -> { + if (combatEnchant instanceof BowEnchant) return; + combatEnchant.use(e, damager, victim, weapon, level); + }); + } + + // --------------------------------------------------------------- + // Armor Defensive Enchants + // --------------------------------------------------------------- + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onEnchantCombatArmor(EntityDamageByEntityEvent e) { + // Prevent armor enchants to have effect if damage is from Thorns. + if (e.getCause() == DamageCause.THORNS) return; + + Entity eVictim = e.getEntity(); + if (!(eVictim instanceof LivingEntity victim)) return; + + Entity eDamager = e.getDamager(); + if (eDamager instanceof Projectile projectile) { + if (projectile.getShooter() instanceof Entity) { + eDamager = (Entity) projectile.getShooter(); + } + } + if (!(eDamager instanceof LivingEntity damager) || eDamager.equals(eVictim)) return; + + EntityEquipment equipDamager = damager.getEquipment(); + if (equipDamager == null) return; + + ItemStack weaponDamager = equipDamager.getItemInMainHand(); + + for (ItemStack armor : EntityUtil.getArmor(victim)) { + if (armor == null || armor.getType().isAir()) continue; + + EnchantManager.getItemCustomEnchants(armor, CombatEnchant.class).forEach((combatEnchant, level) -> { + combatEnchant.use(e, damager, victim, weaponDamager, level); + }); + } + } + + // --------------------------------------------------------------- + // Bow Shooting Enchants + // --------------------------------------------------------------- + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onEnchantBowShoot(EntityShootBowEvent e) { + LivingEntity shooter = e.getEntity(); + if (shooter.getEquipment() == null) return; + + ItemStack bow = e.getBow(); + if (bow == null || bow.getType().isAir() || bow.getType() == Material.ENCHANTED_BOOK) return; + + EnchantManager.getItemCustomEnchants(bow, BowEnchant.class).forEach((bowEnchant, level) -> { + bowEnchant.use(e, shooter, bow, level); + }); + + if (e.getProjectile() instanceof Projectile projectile) { + EnchantManager.setArrowWeapon(projectile, bow); + } + } + + // --------------------------------------------------------------- + // Bow Damage Enchants + // --------------------------------------------------------------- + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onEnchantBowDamage(EntityDamageByEntityEvent e) { + if (!(e.getEntity() instanceof LivingEntity victim)) return; + if (!(e.getDamager() instanceof Projectile projectile)) return; + if (!(projectile.getShooter() instanceof LivingEntity damager)) return; + + ItemStack bow = EnchantManager.getArrowWeapon(projectile); + if (bow == null || bow.getType().isAir() || bow.getType() == Material.ENCHANTED_BOOK) return; + + EnchantManager.getItemCustomEnchants(bow, BowEnchant.class).forEach((bowEnchant, level) -> { + bowEnchant.use(e, damager, victim, bow, level); + }); + } + + // --------------------------------------------------------------- + // Bow Hit Land Enchants + // --------------------------------------------------------------- + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onEnchantBowHit(ProjectileHitEvent e) { + Projectile projectile = e.getEntity(); + + ItemStack bow = EnchantManager.getArrowWeapon(projectile); + if (bow == null || bow.getType().isAir() || bow.getType() == Material.ENCHANTED_BOOK) return; + + EnchantManager.getItemCustomEnchants(bow, BowEnchant.class).forEach((bowEnchant, level) -> { + bowEnchant.use(e, projectile, bow, level); + }); + } + + // --------------------------------------------------------------- + // Interaction Related Enchants + // --------------------------------------------------------------- + @EventHandler(priority = EventPriority.HIGHEST) + public void onEnchantInteract(PlayerInteractEvent e) { + if (e.useInteractedBlock() == Result.DENY) return; + if (e.useItemInHand() == Result.DENY) return; + + ItemStack item = e.getItem(); + if (item == null || item.getType().isAir() || item.getType() == Material.ENCHANTED_BOOK) return; + + Player player = e.getPlayer(); + EnchantManager.getItemCustomEnchants(item, InteractEnchant.class).forEach((interEnchant, level) -> { + interEnchant.use(e, player, item, level); + }); + } + + // --------------------------------------------------------------- + // Death Related Enchants + // --------------------------------------------------------------- + @EventHandler(priority = EventPriority.HIGHEST) + public void onEnchantDeath(EntityDeathEvent e) { + LivingEntity dead = e.getEntity(); + for (ItemStack armor : EntityUtil.getArmor(dead)) { + if (armor == null || armor.getType().isAir()) continue; + + EnchantManager.getItemCustomEnchants(armor, DeathEnchant.class).forEach((deathEnchant, level) -> { + deathEnchant.use(e, dead, level); + }); + } + + Player killer = dead.getKiller(); + if (killer == null) return; + + ItemStack weapon = killer.getInventory().getItemInMainHand(); + if (weapon.getType().isAir() || weapon.getType() == Material.ENCHANTED_BOOK) return; + + EnchantManager.getItemCustomEnchants(weapon, DeathEnchant.class).forEach((deathEnchant, level) -> { + deathEnchant.use(e, dead, level); + }); + } + + // Handle BlockBreak enchantments. + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void onEnchantBlockBreak(BlockBreakEvent e) { + Player player = e.getPlayer(); + if (player.getGameMode() == GameMode.CREATIVE) return; + + ItemStack tool = player.getInventory().getItemInMainHand(); + if (tool.getType().isAir() || tool.getType() == Material.ENCHANTED_BOOK) return; + + EnchantManager.getItemCustomEnchants(tool, BlockBreakEnchant.class).forEach((blockEnchant, level) -> { + blockEnchant.use(e, player, tool, level); + }); + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/object/EnchantListGUI.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/object/EnchantListGUI.java new file mode 100644 index 0000000..79aae1f --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/object/EnchantListGUI.java @@ -0,0 +1,123 @@ +package su.nightexpress.excellentenchants.manager.object; + +import org.bukkit.NamespacedKey; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.config.JYML; +import su.nexmedia.engine.api.menu.*; +import su.nexmedia.engine.utils.CollectionsUtil; +import su.nexmedia.engine.utils.ItemUtil; +import su.nexmedia.engine.utils.PDCUtil; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.ExcellentEnchant; +import su.nightexpress.excellentenchants.manager.EnchantRegister; + +import java.util.*; + +public class EnchantListGUI extends AbstractMenu { + + private final ItemStack enchantIcon; + private final int[] enchantSlots; + + private final NamespacedKey keyLevel; + private final Map> iconCache; + + public EnchantListGUI(@NotNull ExcellentEnchants plugin) { + super(plugin, JYML.loadOrExtract(plugin, "gui.enchants.yml"), ""); + this.keyLevel = new NamespacedKey(plugin, "list_display_level"); + this.iconCache = new HashMap<>(); + + this.enchantIcon = cfg.getItem("Enchantments.Icon"); + this.enchantSlots = cfg.getIntArray("Enchantments.Slots"); + + IMenuClick click = (p, type, e) -> { + if (type instanceof MenuItemType type2) { + switch (type2) { + case PAGE_NEXT -> this.open(p, this.getPage(p) + 1); + case PAGE_PREVIOUS -> this.open(p, this.getPage(p) - 1); + case CLOSE -> p.closeInventory(); + } + } + }; + + for (String sId : cfg.getSection("Content")) { + IMenuItem menuItem = cfg.getMenuItem("Content." + sId); + + if (menuItem.getType() != null) { + menuItem.setClick(click); + } + this.addItem(menuItem); + } + } + + private ItemStack getEnchantIcon(@NotNull ExcellentEnchant enchant, int level) { + return this.iconCache.computeIfAbsent(enchant.getId(), k -> new HashMap<>()).computeIfAbsent(level, k -> this.buildEnchantIcon(enchant, level)); + } + + @NotNull + private ItemStack buildEnchantIcon(@NotNull ExcellentEnchant enchant, int level) { + ItemStack icon = new ItemStack(this.enchantIcon); + + // Override the conflicts placeholder display to make it in a list. + List conflicts = enchant.getConflicts().isEmpty() + ? plugin.lang().Other_None.asList() + : enchant.getConflicts().stream().filter(Objects::nonNull) + .map(en -> plugin.lang().getEnchantment(en)).toList(); + + ItemUtil.replaceLore(icon, ExcellentEnchant.PLACEHOLDER_CONFLICTS, conflicts); + ItemUtil.replace(icon, enchant.formatString(level)); + return icon; + } + + @Override + public void onPrepare(@NotNull Player player, @NotNull Inventory inventory) { + int page = this.getPage(player); + int length = this.enchantSlots.length; + List list = new ArrayList<>(EnchantRegister.ENCHANT_LIST.stream(). + sorted(Comparator.comparing(ExcellentEnchant::getName)).toList()); + List> split = CollectionsUtil.split(list, length); + + int pages = split.size(); + if (pages < 1 || pages < page) list = Collections.emptyList(); + else list = split.get(page - 1); + + int count = 0; + for (ExcellentEnchant enchant : list) { + ItemStack icon = this.getEnchantIcon(enchant, 1); + PDCUtil.setData(icon, this.keyLevel, 1); + + IMenuClick click = (p, type, e) -> { + if (!e.isLeftClick()) return; + + ItemStack itemClick = e.getCurrentItem(); + if (itemClick == null) return; + + int levelHas = PDCUtil.getIntData(itemClick, this.keyLevel); + if (levelHas == 0) return; + + if (++levelHas > enchant.getMaxLevel()) levelHas = enchant.getStartLevel(); + itemClick = this.getEnchantIcon(enchant, levelHas); + PDCUtil.setData(itemClick, this.keyLevel, levelHas); + + e.setCurrentItem(itemClick); + }; + + IMenuItem menuItem = new MenuItem(icon, this.enchantSlots[count++]); + menuItem.setClick(click); + this.addItem(player, menuItem); + } + this.setPage(player, page, pages); + } + + @Override + public void onReady(@NotNull Player player, @NotNull Inventory inventory) { + + } + + @Override + public boolean cancelClick(@NotNull SlotType slotType, int slot) { + return true; + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/object/EnchantScaler.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/object/EnchantScaler.java new file mode 100644 index 0000000..a0dfb67 --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/object/EnchantScaler.java @@ -0,0 +1,12 @@ +package su.nightexpress.excellentenchants.manager.object; + +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.manager.leveling.Scaler; +import su.nightexpress.excellentenchants.api.enchantment.ExcellentEnchant; + +public class EnchantScaler extends Scaler { + + public EnchantScaler(@NotNull ExcellentEnchant enchant, @NotNull String path) { + super(enchant.getConfig(), path, ExcellentEnchant.PLACEHOLDER_LEVEL, enchant.getStartLevel(), enchant.getMaxLevel()); + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/object/EnchantTier.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/object/EnchantTier.java new file mode 100644 index 0000000..ddaa01e --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/object/EnchantTier.java @@ -0,0 +1,94 @@ +package su.nightexpress.excellentenchants.manager.object; + +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import su.nexmedia.engine.utils.StringUtil; +import su.nexmedia.engine.utils.random.Rnd; +import su.nightexpress.excellentenchants.api.enchantment.ExcellentEnchant; +import su.nightexpress.excellentenchants.manager.type.ObtainType; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +public class EnchantTier { + + private final String id; + private final int priority; + private final String name; + private final String color; + private final Map chance; + + private final Set enchants; + + public EnchantTier(@NotNull String id, int priority, @NotNull String name, @NotNull String color, @NotNull Map chance) { + this.id = id.toLowerCase(); + this.priority = priority; + this.name = StringUtil.color(name); + this.color = StringUtil.color(color); + this.chance = chance; + this.enchants = new HashSet<>(); + } + + @NotNull + public String getId() { + return this.id; + } + + public int getPriority() { + return priority; + } + + @NotNull + public String getName() { + return this.name; + } + + @NotNull + public String getColor() { + return this.color; + } + + @NotNull + public Map getChance() { + return this.chance; + } + + public double getChance(@NotNull ObtainType obtainType) { + return this.getChance().getOrDefault(obtainType, 0D); + } + + @NotNull + public Set getEnchants() { + return this.enchants; + } + + @NotNull + public Set getEnchants(@NotNull ObtainType obtainType) { + return this.getEnchants(obtainType, null); + } + + @NotNull + public Set getEnchants(@NotNull ObtainType obtainType, @Nullable ItemStack item) { + Set set = this.getEnchants().stream() + .filter(en -> en.getObtainChance(obtainType) > 0) + .filter(en -> item == null || en.canEnchantItem(item)).collect(Collectors.toSet()); + set.removeIf(en -> obtainType == ObtainType.ENCHANTING && en.isTreasure()); + return set; + } + + @Nullable + public ExcellentEnchant getEnchant(@NotNull ObtainType obtainType) { + return getEnchant(obtainType, null); + } + + @Nullable + public ExcellentEnchant getEnchant(@NotNull ObtainType obtainType, @Nullable ItemStack item) { + Map map = this.getEnchants(obtainType).stream() + .filter(en -> item == null || en.canEnchantItem(item)) + .collect(Collectors.toMap(k -> k, v -> v.getObtainChance(obtainType))); + return map.isEmpty() ? null : Rnd.get(map); + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/tasks/AbstractEnchantPassiveTask.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/tasks/AbstractEnchantPassiveTask.java new file mode 100644 index 0000000..9cf72ed --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/tasks/AbstractEnchantPassiveTask.java @@ -0,0 +1,56 @@ +package su.nightexpress.excellentenchants.manager.tasks; + +import org.bukkit.Material; +import org.bukkit.entity.LivingEntity; +import org.bukkit.inventory.EntityEquipment; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.task.AbstractTask; +import su.nexmedia.engine.utils.EntityUtil; +import su.nexmedia.engine.utils.ItemUtil; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.config.Config; + +import java.util.*; + +public abstract class AbstractEnchantPassiveTask extends AbstractTask { + + public AbstractEnchantPassiveTask(@NotNull ExcellentEnchants plugin, long interval, boolean async) { + super(plugin, interval, async); + } + + protected abstract void apply(@NotNull LivingEntity entity, @NotNull ItemStack armor, @NotNull ItemMeta meta); + + @Override + public void action() { + for (LivingEntity entity : this.getEntities()) { + + List list = new ArrayList<>(Arrays.asList(EntityUtil.getArmor(entity))); + EntityEquipment equipment = entity.getEquipment(); + if (equipment != null && !ItemUtil.isArmor(equipment.getItemInMainHand())) { + list.add(equipment.getItemInMainHand()); + } + list.removeIf(armor -> armor == null || armor.getType().isAir() || armor.getType() == Material.ENCHANTED_BOOK); + + for (ItemStack armor : list) { + ItemMeta meta = armor.getItemMeta(); + if (meta == null) continue; + + this.apply(entity, armor, meta); + } + } + } + + @NotNull + protected Collection<@NotNull ? extends LivingEntity> getEntities() { + Set list = new HashSet<>(plugin.getServer().getOnlinePlayers()); + + if (Config.ENCHANTMENTS_ENTITY_PASSIVE_FOR_MOBS) { + plugin.getServer().getWorlds().forEach(world -> { + list.addAll(world.getEntitiesByClass(LivingEntity.class)); + }); + } + return list; + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/tasks/ArrowTrailsTask.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/tasks/ArrowTrailsTask.java new file mode 100644 index 0000000..90a60e4 --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/tasks/ArrowTrailsTask.java @@ -0,0 +1,37 @@ +package su.nightexpress.excellentenchants.manager.tasks; + +import org.bukkit.entity.Projectile; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.api.task.AbstractTask; +import su.nexmedia.engine.utils.EffectUtil; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.config.Config; + +import java.util.*; + +public class ArrowTrailsTask extends AbstractTask { + + private static final Map>> TRAILS_MAP = Collections.synchronizedMap(new HashMap<>()); + + public ArrowTrailsTask(@NotNull ExcellentEnchants plugin) { + super(plugin, Config.TASKS_ARROW_TRAIL_TICKS_INTERVAL, true); + TRAILS_MAP.clear(); + } + + @Override + public void action() { + TRAILS_MAP.keySet().removeIf(projectile -> !projectile.isValid() || projectile.isDead()); + + TRAILS_MAP.forEach((arrow, effects) -> { + effects.forEach(entry -> { + EffectUtil.playEffect(arrow.getLocation(), entry.getKey(), entry.getValue(), 0f, 0f, 0f, 0f, 10); + }); + }); + } + + public static void add(@NotNull Projectile projectile, @NotNull String particleName, @NotNull String particleData) { + synchronized (TRAILS_MAP) { + TRAILS_MAP.computeIfAbsent(projectile, list -> new HashSet<>()).add(new AbstractMap.SimpleEntry<>(particleName, particleData)); + } + } +} \ No newline at end of file diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/tasks/EnchantEffectPassiveTask.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/tasks/EnchantEffectPassiveTask.java new file mode 100644 index 0000000..c9d35ce --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/tasks/EnchantEffectPassiveTask.java @@ -0,0 +1,28 @@ +package su.nightexpress.excellentenchants.manager.tasks; + +import org.bukkit.entity.LivingEntity; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.NotNull; +import su.nightexpress.excellentenchants.ExcellentEnchants; +import su.nightexpress.excellentenchants.api.enchantment.IEnchantPotionTemplate; +import su.nightexpress.excellentenchants.api.enchantment.type.PassiveEnchant; +import su.nightexpress.excellentenchants.config.Config; + +public class EnchantEffectPassiveTask extends AbstractEnchantPassiveTask { + + public EnchantEffectPassiveTask(@NotNull ExcellentEnchants plugin) { + super(plugin, Config.TASKS_PASSIVE_ENCHANTS_TICKS_INTERVAL, true); + } + + @Override + protected void apply(@NotNull LivingEntity entity, @NotNull ItemStack armor, @NotNull ItemMeta meta) { + meta.getEnchants().forEach((enchantment, level) -> { + if (level < 1) return; + if (!(enchantment instanceof PassiveEnchant passiveEnchant)) return; + if (!(enchantment instanceof IEnchantPotionTemplate)) return; + + passiveEnchant.use(entity, level); + }); + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/type/FitItemType.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/type/FitItemType.java new file mode 100644 index 0000000..7a47feb --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/type/FitItemType.java @@ -0,0 +1,57 @@ +package su.nightexpress.excellentenchants.manager.type; + +import org.bukkit.Material; +import org.bukkit.enchantments.EnchantmentTarget; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import su.nexmedia.engine.utils.ItemUtil; +import su.nightexpress.excellentenchants.config.Config; + +public enum FitItemType { + + HELMET, CHESTPLATE, LEGGINGS, BOOTS, ELYTRA, + WEAPON, TOOL, ARMOR, + SWORD, TRIDENT, AXE, BOW, CROSSBOW, + HOE, PICKAXE, SHOVEL, FISHING_ROD; + + @Nullable + public static FitItemType getByEnchantmentTarget(@NotNull EnchantmentTarget target) { + return switch (target) { + case ARMOR -> ARMOR; + case ARMOR_FEET -> BOOTS; + case ARMOR_LEGS -> LEGGINGS; + case ARMOR_TORSO -> CHESTPLATE; + case ARMOR_HEAD -> HELMET; + case WEAPON -> WEAPON; + case TOOL -> TOOL; + case BOW -> BOW; + case FISHING_ROD -> FISHING_ROD; + case TRIDENT -> TRIDENT; + case CROSSBOW -> CROSSBOW; + default -> null; + }; + } + + public boolean isIncluded(@NotNull ItemStack item) { + return switch (this) { + case HELMET -> ItemUtil.isHelmet(item); + case CHESTPLATE -> ItemUtil.isChestplate(item) || (Config.ENCHANTMENTS_ITEM_ELYTRA_AS_CHESTPLATE && ELYTRA.isIncluded(item)); + case LEGGINGS -> ItemUtil.isLeggings(item); + case BOOTS -> ItemUtil.isBoots(item); + case ELYTRA -> item.getType() == Material.ELYTRA; + case WEAPON -> ItemUtil.isWeapon(item); + case TOOL -> ItemUtil.isTool(item); + case ARMOR -> ItemUtil.isArmor(item); + case SWORD -> ItemUtil.isSword(item) || (Config.ENCHANTMENTS_ITEM_AXES_AS_SWORDS && AXE.isIncluded(item)); + case TRIDENT -> ItemUtil.isTrident(item); + case AXE -> ItemUtil.isAxe(item); + case BOW -> item.getType() == Material.BOW || (Config.ENCHANTMENTS_ITEM_CROSSBOWS_AS_BOWS && CROSSBOW.isIncluded(item)); + case CROSSBOW -> item.getType() == Material.CROSSBOW; + case HOE -> ItemUtil.isHoe(item); + case PICKAXE -> ItemUtil.isPickaxe(item); + case SHOVEL -> ItemUtil.isShovel(item); + case FISHING_ROD -> item.getType() == Material.FISHING_ROD; + }; + } +} diff --git a/Core/src/main/java/su/nightexpress/excellentenchants/manager/type/ObtainType.java b/Core/src/main/java/su/nightexpress/excellentenchants/manager/type/ObtainType.java new file mode 100644 index 0000000..7bd0f1c --- /dev/null +++ b/Core/src/main/java/su/nightexpress/excellentenchants/manager/type/ObtainType.java @@ -0,0 +1,23 @@ +package su.nightexpress.excellentenchants.manager.type; + +import org.jetbrains.annotations.NotNull; + +public enum ObtainType { + + ENCHANTING("Enchanting_Table"), + VILLAGER("Villagers"), + LOOT_GENERATION("Loot_Generation"), + FISHING("Fishing"), + MOB_SPAWNING("Mob_Spawning"); + + private final String pathName; + + ObtainType(@NotNull String pathName) { + this.pathName = pathName; + } + + @NotNull + public String getPathName() { + return pathName; + } +} diff --git a/Core/src/main/resources/config.yml b/Core/src/main/resources/config.yml new file mode 100644 index 0000000..d318b31 --- /dev/null +++ b/Core/src/main/resources/config.yml @@ -0,0 +1,165 @@ +# Plugin engine settings. +Plugin: + # Name(s) for the main plugin command. + Command_Aliases: excellentenchants,eenchants + # Plugin language config. + # + # In 'lang' folder there are configs for different languages with a format like 'messages_code.yml', + # where 'code' is the language code which you specify here. + # + # To create a new language config, simply copy the default one and rename it to your language code. + Language: en + # Plugin prefix name. + # To change a prefix format, edit language config in '/NexEngine/lang'. + Prefix: ExcellentEnchants + +# General plugin settings. +General: + # Settings for the plugin tasks. + Tasks: + Arrow_Trails: + # Defines the ticks interval (20 ticks = 1 second) for the arrow particle effect spawning. + Ticks_Interval: 1 + Passive_Enchants: + # Defines the ticks interval (20 ticks = 1 second) for the passive enchantment effects to trigger. + Ticks_Interval: 100 + # Enchantment global settings. + Enchantments: + # List of disabled custom enchantments. + # Use enchantment file names from the 'enchants' folder without the file extension. + # For example, to disable 'Explosive Arrows' enchantment you have to add 'explosive_arrows' to this list. + Disabled: + - enchant_name + - other_enchant + # Here you can disable certain enchantments in certain worlds. + Disabled_In_Worlds: + # This is your world name with a list of disabled enchantments. + # Enchantment names are the same as in the option above. + # To disable all enchantments, use '*'. + my_world: + - '*' + other_world: + - 'enchant_name' + - 'another_enchant' + # Item settings. + Item: + # How many of custom enchantments the item can contain at the same time? + Max_Custom_Enchants: 3 + # Set this to 'true' to make Sword enchantments apply on Axes. + Axes_As_Swords: true + # Set this to 'true' to make Bow enchantments apply on Crossbows. + Crossbows_As_Bows: true + # Set this to 'true' to make Chestplate enchantments apply on Elytras. + Elytra_As_Chestplate: true + # Non-player entity settings. + Entity: + # When enabled, enchantments with a passive effects (potion effects, regeneration, etc.) + # will be applied to mobs as well (if they are wearing items with such enchantments). + # Enabling this feature may cause performance issues. + Passive_Enchants_Applied_To_Mobs: false + # Enchantment description settings. + Description: + # When enabled, each custom enchantment will add a description in item lore. + Enabled: true + # Description format for the item lore. + # Placeholders: + # - %description% - Enchantment description. + Format: '&8▸ %description%' + # Settings to obtain enchants via Enchanting Table. + Enchanting_Table: + # Enable/Disable this way to obtain custom enchantments. + Enabled: true + Enchantments: + # Max. amount of all (including vanilla) enchantments on the item, when no more enchantments will be added. + Total_Maximum: 4 + # Chance that a custom enchantment will be added. + Custom_Generation_Chance: 90.0 + # Min. amount of custom enchantments to be added. + Custom_Minimum: 0 + # Max. amount of custom enchantments to be added. + Custom_Maximum: 2 + # Settings to obtain enchantments via Villager Trades. + Villagers: + Enabled: true + Enchantments: + Total_Maximum: 4 + Custom_Generation_Chance: 70.0 + Custom_Minimum: 0 + Custom_Maximum: 2 + # Settings to obtain enchantments via Loot Chests in dungeons and other world's structures. + Loot_Generation: + Enabled: true + Enchantments: + Total_Maximum: 4 + Custom_Generation_Chance: 80.0 + Custom_Minimum: 0 + Custom_Maximum: 2 + # Settings to obtain enchantments via Fishing. + Fishing: + Enabled: true + Enchantments: + Total_Maximum: 4 + Custom_Generation_Chance: 70.0 + Custom_Minimum: 0 + Custom_Maximum: 2 + # Settings to generate enchantments on mob equipment on spawn. + Mob_Spawning: + Enabled: true + Enchantments: + Total_Maximum: 4 + Custom_Generation_Chance: 70.0 + Custom_Minimum: 0 + Custom_Maximum: 2 + +# Enchantment tiers. +# You can create as many tiers as you want. +Tiers: + # Tier identifier. Must be unique. + common: + # Tier display name. + Name: '&fCommon' + # Tier color. + Color: '&f' + # Tier obtain chances. + Obtain_Chance: + ENCHANTING: 80.0 + VILLAGER: 80.0 + LOOT_GENERATION: 80.0 + FISHING: 80.0 + MOB_SPAWNING: 80.0 + rare: + Name: '&aRare' + Color: '&a' + Obtain_Chance: + ENCHANTING: 50.0 + VILLAGER: 50.0 + LOOT_GENERATION: 50.0 + FISHING: 50.0 + MOB_SPAWNING: 50.0 + exotic: + Name: '&eExotic' + Color: '&e' + Obtain_Chance: + ENCHANTING: 25.0 + VILLAGER: 25.0 + LOOT_GENERATION: 25.0 + FISHING: 25.0 + MOB_SPAWNING: 25.0 + legendary: + Name: '&6Legendary' + Color: '&6' + Obtain_Chance: + ENCHANTING: 10.0 + VILLAGER: 10.0 + LOOT_GENERATION: 10.0 + FISHING: 10.0 + MOB_SPAWNING: 10.0 + cursed: + Name: '&cCursed' + Color: '&c' + Obtain_Chance: + ENCHANTING: 7.0 + VILLAGER: 5.0 + LOOT_GENERATION: 7.0 + FISHING: 12.0 + MOB_SPAWNING: 0.0 \ No newline at end of file diff --git a/Core/src/main/resources/enchants/aquaman.yml b/Core/src/main/resources/enchants/aquaman.yml new file mode 100644 index 0000000..40af9a7 --- /dev/null +++ b/Core/src/main/resources/enchants/aquaman.yml @@ -0,0 +1,31 @@ +Name: 'Aquaman' +Tier: 'exotic' +Description: + - 'Grants %enchantment_potion_type% %enchantment_potion_level% effect that costs x1 %enchantment_cost_item%.' +Is_Treasure: false +Level: + Min: 1 + Max: 1 +Anvil: + Merge_Cost: '%enchantment_level%' +Enchanting_Table: + Level_By_Exp_Cost: '%enchantment_level% * 15.0' + Chance: 20.0 +Villagers: + Chance: 20.0 +Loot_Generation: + Chance: 25.0 +Fishing: + Chance: 30.0 +Mob_Spawning: + Chance: 0.0 +Settings: + Trigger_Chance: '100.0' + Cost: + Enabled: true + Item: + Material: PRISMARINE_SHARD + Amount: 1 + Potion_Effect: + Level: '1' + Duration: '25.0' \ No newline at end of file diff --git a/Core/src/main/resources/enchants/bane_of_netherspawn.yml b/Core/src/main/resources/enchants/bane_of_netherspawn.yml new file mode 100644 index 0000000..a8499d5 --- /dev/null +++ b/Core/src/main/resources/enchants/bane_of_netherspawn.yml @@ -0,0 +1,34 @@ +Name: Bane of Netherspawn +Tier: common +Description: + - '%enchantment_trigger_chance%% chance to inflict %enchantment_damage% more damage to Nether mobs.' +Is_Treasure: false +Level: + Min: 1 + Max: 5 +Anvil: + Merge_Cost: '%enchantment_level%' +Fishing: + Chance: 25.0 +Enchanting_Table: + Level_By_Exp_Cost: '6 * %enchantment_level%' + Chance: 80.0 +Villagers: + Chance: 80.0 +Loot_Generation: + Chance: 80.0 +Mob_Spawning: + Chance: 50.0 +Settings: + Trigger_Chance: '100.0' + Cost: + Enabled: false + Item: + Material: AIR + Amount: 1 + Particle: + Name: 'SMOKE_NORMAL' + Data: '' + Damage: + As_Modifier: false + Formula: '0.5 * %enchantment_level%' diff --git a/Core/src/main/resources/enchants/blast_mining.yml b/Core/src/main/resources/enchants/blast_mining.yml new file mode 100644 index 0000000..65fa1c1 --- /dev/null +++ b/Core/src/main/resources/enchants/blast_mining.yml @@ -0,0 +1,31 @@ +Is_Treasure: false +Name: Blast Mining +Tier: legendary +Description: + - '%enchantment_trigger_chance%% chance to mine blocks by x%enchantment_explosion_power% power explosion.' +Level: + Min: 1 + Max: 5 +Anvil: + Merge_Cost: '%enchantment_level%' +Fishing: + Chance: 5.0 +Enchanting_Table: + Level_By_Exp_Cost: '6.0 * %enchantment_level%' + Chance: 10.0 +Villagers: + Chance: 10.0 +Loot_Generation: + Chance: 20.0 +Mob_Spawning: + Chance: 0.0 +Settings: + Trigger_Chance: '20.0 * %enchantment_level%' + Min_Block_Strength: '1.5 - %enchantment_level% / 10' + Cost: + Enabled: false + Item: + Material: AIR + Amount: 1 + Explosion: + Power: '3.0 + (%enchantment_level% - 1.0 * 0.25)' diff --git a/Core/src/main/resources/enchants/blindness.yml b/Core/src/main/resources/enchants/blindness.yml new file mode 100644 index 0000000..ece0797 --- /dev/null +++ b/Core/src/main/resources/enchants/blindness.yml @@ -0,0 +1,34 @@ +Is_Treasure: false +Name: Blindness +Tier: common +Description: + - '%enchantment_trigger_chance%% chance to apply %enchantment_potion_type% %enchantment_potion_level% for %enchantment_potion_duration%s. on hit.' +Level: + Min: 1 + Max: 3 +Anvil: + Merge_Cost: '%enchantment_level%' +Fishing: + Chance: 60.0 +Enchanting_Table: + Level_By_Exp_Cost: '9 * %enchantment_level%' + Chance: 60.0 +Villagers: + Chance: 60.0 +Loot_Generation: + Chance: 60.0 +Mob_Spawning: + Chance: 30.0 +Settings: + Trigger_Chance: '15.0 + %enchantment_level% * 3' + Cost: + Enabled: false + Item: + Material: AIR + Amount: 1 + Potion_Effect: + Duration: '3.5 + %enchantment_level%' + Level: '%enchantment_level%' + Particle: + Name: SMOKE_NORMAL + Data: '' diff --git a/Core/src/main/resources/enchants/bomber.yml b/Core/src/main/resources/enchants/bomber.yml new file mode 100644 index 0000000..16bc189 --- /dev/null +++ b/Core/src/main/resources/enchants/bomber.yml @@ -0,0 +1,29 @@ +Is_Treasure: false +Name: Bomber +Tier: exotic +Description: + - '%enchantment_trigger_chance%% chance to launch TNT that explodes in %enchantment_fuse_ticks%s.' +Level: + Min: 1 + Max: 3 +Anvil: + Merge_Cost: '%enchantment_level%' +Fishing: + Chance: 20.0 +Enchanting_Table: + Level_By_Exp_Cost: '9 * %enchantment_level%' + Chance: 20.0 +Villagers: + Chance: 20.0 +Loot_Generation: + Chance: 20.0 +Mob_Spawning: + Chance: 5.0 +Settings: + Trigger_Chance: '4.0 * %enchantment_level%' + Cost: + Enabled: false + Item: + Material: TNT + Amount: 1 + Fuse_Ticks: '100 - %enchantment_level% * 10' \ No newline at end of file diff --git a/Core/src/main/resources/enchants/bunny_hop.yml b/Core/src/main/resources/enchants/bunny_hop.yml new file mode 100644 index 0000000..dc4491f --- /dev/null +++ b/Core/src/main/resources/enchants/bunny_hop.yml @@ -0,0 +1,30 @@ +Is_Treasure: false +Name: Bunny Hop +Tier: common +Description: + - 'Grants permanent %enchantment_potion_type% %enchantment_potion_level%.' +Level: + Min: 1 + Max: 3 +Anvil: + Merge_Cost: '%enchantment_level%' +Fishing: + Chance: 50.0 +Enchanting_Table: + Level_By_Exp_Cost: '9 * %enchantment_level%' + Chance: 50.0 +Villagers: + Chance: 50.0 +Loot_Generation: + Chance: 50.0 +Mob_Spawning: + Chance: 25.0 +Settings: + Trigger_Chance: '100.0' + Cost: + Enabled: false + Item: + Material: RABBIT_FOOT + Potion_Effect: + Duration: '25.0' + Level: '%enchantment_level%' diff --git a/Core/src/main/resources/enchants/cold_steel.yml b/Core/src/main/resources/enchants/cold_steel.yml new file mode 100644 index 0000000..e6fff9d --- /dev/null +++ b/Core/src/main/resources/enchants/cold_steel.yml @@ -0,0 +1,31 @@ +Is_Treasure: false +Name: Cold Steel +Tier: rare +Description: + - '%enchantment_trigger_chance%% chance to apply %enchantment_potion_type% %enchantment_potion_level% for %enchantment_potion_duration%s. to attacker.' +Level: + Min: 1 + Max: 3 +Anvil: + Merge_Cost: '%enchantment_level%' +Fishing: + Chance: 50.0 +Enchanting_Table: + Level_By_Exp_Cost: '9 * %enchantment_level%' + Chance: 50.0 +Villagers: + Chance: 50.0 +Loot_Generation: + Chance: 50.0 +Mob_Spawning: + Chance: 30.0 +Settings: + Trigger_Chance: '60.0 + %enchantment_level% * 5' + Cost: + Enabled: false + Item: + Material: AIR + Amount: 1 + Potion_Effect: + Duration: '4.0 + %enchantment_level%' + Level: '%enchantment_level%' diff --git a/Core/src/main/resources/enchants/confusion.yml b/Core/src/main/resources/enchants/confusion.yml new file mode 100644 index 0000000..d3d0898 --- /dev/null +++ b/Core/src/main/resources/enchants/confusion.yml @@ -0,0 +1,34 @@ +Is_Treasure: false +Name: Confusion +Tier: common +Description: + - '%enchantment_trigger_chance%% chance to apply %enchantment_potion_type% %enchantment_potion_level% for %enchantment_potion_duration%s. on hit.' +Level: + Min: 1 + Max: 3 +Anvil: + Merge_Cost: '%enchantment_level%' +Fishing: + Chance: 50.0 +Enchanting_Table: + Level_By_Exp_Cost: '9 * %enchantment_level%' + Chance: 50.0 +Villagers: + Chance: 50.0 +Loot_Generation: + Chance: 50.0 +Mob_Spawning: + Chance: 30.0 +Settings: + Trigger_Chance: '15.0 * %enchantment_level%' + Cost: + Enabled: false + Item: + Material: AIR + Amount: 1 + Potion_Effect: + Duration: '5.0 + %enchantment_level% * 1.5' + Level: '%enchantment_level%' + Particle: + Name: 'ITEM_CRACK' + Data: 'ROTTEN_FLESH' diff --git a/Core/src/main/resources/enchants/cure.yml b/Core/src/main/resources/enchants/cure.yml new file mode 100644 index 0000000..36287c7 --- /dev/null +++ b/Core/src/main/resources/enchants/cure.yml @@ -0,0 +1,32 @@ +Is_Treasure: false +Name: Cure +Tier: exotic +Description: + - '%enchantment_trigger_chance%% chance to cure Zombified Piglins and Zombie Villagers.' +Level: + Min: 1 + Max: 5 +Anvil: + Merge_Cost: '%enchantment_level%' +Fishing: + Chance: 60.0 +Enchanting_Table: + Level_By_Exp_Cost: '5 * %enchantment_level%' + Chance: 60.0 +Villagers: + Chance: 75.0 +Loot_Generation: + Chance: 50.0 +Mob_Spawning: + Chance: 0.0 +Settings: + Trigger_Chance: '20.0 + %enchantment_level% * 8' + Cost: + Enabled: false + Item: + Material: AIR + Amount: 1 + Particle: + Name: 'CLOUD' + Data: '' + Sound: '' diff --git a/Core/src/main/resources/enchants/curse_of_breaking.yml b/Core/src/main/resources/enchants/curse_of_breaking.yml new file mode 100644 index 0000000..c50b046 --- /dev/null +++ b/Core/src/main/resources/enchants/curse_of_breaking.yml @@ -0,0 +1,29 @@ +Is_Treasure: true +Name: Curse of Breaking +Tier: cursed +Description: + - '%enchantment_trigger_chance%% chance the item will consume extra %enchantment_durability_amount% durability points.' +Level: + Min: 1 + Max: 3 +Anvil: + Merge_Cost: '%enchantment_level%' +Fishing: + Chance: 20.0 +Enchanting_Table: + Level_By_Exp_Cost: '15.0 * %enchantment_level%' + Chance: 0.0 +Villagers: + Chance: 20.0 +Loot_Generation: + Chance: 20.0 +Mob_Spawning: + Chance: 0.0 +Settings: + Trigger_Chance: '10.0 * %enchantment_level%' + Durability_Amount: '%enchantment_level%' + Cost: + Enabled: false + Item: + Material: AIR + Amount: 1 \ No newline at end of file diff --git a/Core/src/main/resources/enchants/curse_of_misfortune.yml b/Core/src/main/resources/enchants/curse_of_misfortune.yml new file mode 100644 index 0000000..b258490 --- /dev/null +++ b/Core/src/main/resources/enchants/curse_of_misfortune.yml @@ -0,0 +1,29 @@ +Is_Treasure: true +Name: Curse of Misfortune +Tier: cursed +Description: + - '%enchantment_trigger_chance%% chance to have no drops from blocks or mobs.' +Level: + Min: 1 + Max: 1 +Anvil: + Merge_Cost: '%enchantment_level%' +Fishing: + Chance: 20.0 +Enchanting_Table: + Level_By_Exp_Cost: '15.0 * %enchantment_level%' + Chance: 0.0 +Villagers: + Chance: 20.0 +Loot_Generation: + Chance: 20.0 +Mob_Spawning: + Chance: 0.0 +Settings: + Trigger_Chance: '20.0 * %enchantment_level%' + Drop_Exp: false + Cost: + Enabled: false + Item: + Material: AIR + Amount: 1 \ No newline at end of file diff --git a/Core/src/main/resources/enchants/cutter.yml b/Core/src/main/resources/enchants/cutter.yml new file mode 100644 index 0000000..04c5430 --- /dev/null +++ b/Core/src/main/resources/enchants/cutter.yml @@ -0,0 +1,31 @@ +Is_Treasure: false +Name: Cutter +Tier: exotic +Description: + - '%enchantment_trigger_chance%% chance to throw away enemy''s armor and damage it for %enchantment_durability_damage%%.' +Level: + Min: 1 + Max: 5 +Anvil: + Merge_Cost: '%enchantment_level%' +Fishing: + Chance: 0.0 +Enchanting_Table: + Level_By_Exp_Cost: '6 * %enchantment_level%' + Chance: 20.0 +Villagers: + Chance: 20.0 +Loot_Generation: + Chance: 20.0 +Mob_Spawning: + Chance: 5.0 +Settings: + Trigger_Chance: '1.0 + %enchantment_level% * 0.6' + Cost: + Enabled: false + Item: + Material: AIR + Amount: 1 + Item: + Durability_Reduction: '%enchantment_level% / 100.0' + Sound: ENTITY_ITEM_BREAK diff --git a/Core/src/main/resources/enchants/decapitator.yml b/Core/src/main/resources/enchants/decapitator.yml new file mode 100644 index 0000000..814e8f2 --- /dev/null +++ b/Core/src/main/resources/enchants/decapitator.yml @@ -0,0 +1,104 @@ +Is_Treasure: false +Name: Decapitator +Tier: exotic +Description: + - '%enchantment_trigger_chance%% chance to obtain player''s or mob''s head.' +Level: + Min: 1 + Max: 4 +Anvil: + Merge_Cost: '%enchantment_level%' +Fishing: + Chance: 30.0 +Enchanting_Table: + Level_By_Exp_Cost: '7 * %enchantment_level%' + Chance: 30.0 +Villagers: + Chance: 30.0 +Loot_Generation: + Chance: 30.0 +Mob_Spawning: + Chance: 15.0 +Settings: + Trigger_Chance: '5.0 + %enchantment_level% * 1.75' + Cost: + Enabled: false + Item: + Material: AIR + Amount: 1 + Particle: + Name: 'BLOCK_CRACK' + Data: 'REDSTONE_BLOCK' + Ignored_Entity_Types: + - BAT + - BEE + - ENDER_DRAGON + - WITHER_SKELETON + - WITHER + Head_Item: + Name: '&cHead of &e%entity%' + Textures: + AXOLOTL: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNThkYTFhMGEyYTEzZGQyMDliZmMyNTI5ZDljN2MyOWEyOWRkOWEyM2ZmNGI4MGIzOGI4OTk2MTc3MmU4MDM5ZSJ9fX0= + BAT: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOWU5OWRlZWY5MTlkYjY2YWMyYmQyOGQ2MzAyNzU2Y2NkNTdjN2Y4YjEyYjlkY2E4ZjQxYzNlMGEwNGFjMWNjIn19fQ== + BEE: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMTI3MjRhOWE0Y2RkNjhiYTQ5NDE1NTYwZTViZTQwYjRhMWM0N2NiNWJlMWQ2NmFlZGI1MmEzMGU2MmVmMmQ0NyJ9fX0= + BLAZE: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYjc4ZWYyZTRjZjJjNDFhMmQxNGJmZGU5Y2FmZjEwMjE5ZjViMWJmNWIzNWE0OWViNTFjNjQ2Nzg4MmNiNWYwIn19fQ== + CAT: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZTRiNDVjYmFhMTlmZTNkNjhjODU2Y2QzODQ2YzAzYjVmNTlkZTgxYTQ4MGVlYzkyMWFiNGZhM2NkODEzMTcifX19 + CAVE_SPIDER: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNTYxN2Y3ZGQ1ZWQxNmYzYmQxODY0NDA1MTdjZDQ0MGExNzAwMTViMWNjNmZjYjJlOTkzYzA1ZGUzM2YifX19 + CHICKEN: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMTYzODQ2OWE1OTljZWVmNzIwNzUzNzYwMzI0OGE5YWIxMWZmNTkxZmQzNzhiZWE0NzM1YjM0NmE3ZmFlODkzIn19fQ== + COD: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNzg5MmQ3ZGQ2YWFkZjM1Zjg2ZGEyN2ZiNjNkYTRlZGRhMjExZGY5NmQyODI5ZjY5MTQ2MmE0ZmIxY2FiMCJ9fX0= + COW: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvN2RmYTBhYzM3YmFiYTJhYTI5MGU0ZmFlZTQxOWE2MTNjZDYxMTdmYTU2OGU3MDlkOTAzNzQ3NTNjMDMyZGNiMCJ9fX0= + DOLPHIN: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU5Njg4Yjk1MGQ4ODBiNTViN2FhMmNmY2Q3NmU1YTBmYTk0YWFjNmQxNmY3OGU4MzNmNzQ0M2VhMjlmZWQzIn19fQ== + DONKEY: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNjNhOTc2YzA0N2Y0MTJlYmM1Y2IxOTcxMzFlYmVmMzBjMDA0YzBmYWY0OWQ4ZGQ0MTA1ZmNhMTIwN2VkYWZmMyJ9fX0= + DROWNED: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYzNmN2NjZjYxZGJjM2Y5ZmU5YTYzMzNjZGUwYzBlMTQzOTllYjJlZWE3MWQzNGNmMjIzYjNhY2UyMjA1MSJ9fX0= + ELDER_GUARDIAN: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMWM3OTc0ODJhMTRiZmNiODc3MjU3Y2IyY2ZmMWI2ZTZhOGI4NDEzMzM2ZmZiNGMyOWE2MTM5Mjc4YjQzNmIifX19 + ENDERMAN: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOTZjMGIzNmQ1M2ZmZjY5YTQ5YzdkNmYzOTMyZjJiMGZlOTQ4ZTAzMjIyNmQ1ZTgwNDVlYzU4NDA4YTM2ZTk1MSJ9fX0= + ENDERMITE: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNWExYTA4MzFhYTAzYWZiNDIxMmFkY2JiMjRlNWRmYWE3ZjQ3NmExMTczZmNlMjU5ZWY3NWE4NTg1NSJ9fX0= + EVOKER: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDk1NDEzNWRjODIyMTM5NzhkYjQ3ODc3OGFlMTIxMzU5MWI5M2QyMjhkMzZkZDU0ZjFlYTFkYTQ4ZTdjYmE2In19fQ== + FOX: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDg5NTRhNDJlNjllMDg4MWFlNmQyNGQ0MjgxNDU5YzE0NGEwZDVhOTY4YWVkMzVkNmQzZDczYTNjNjVkMjZhIn19fQ== + GHAST: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZGU4YTM4ZTlhZmJkM2RhMTBkMTliNTc3YzU1YzdiZmQ2YjRmMmU0MDdlNDRkNDAxN2IyM2JlOTE2N2FiZmYwMiJ9fX0= + GOAT: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDU3YTBkNTM4ZmEwOGE3YWZmZTMxMjkwMzQ2ODg2MTcyMGY5ZmEzNGU4NmQ0NGI4OWRjZWM1NjM5MjY1ZjAzIn19fQ== + GUARDIAN: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYTBiZjM0YTcxZTc3MTViNmJhNTJkNWRkMWJhZTVjYjg1Zjc3M2RjOWIwZDQ1N2I0YmZjNWY5ZGQzY2M3Yzk0In19fQ== + HOGLIN: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOWJiOWJjMGYwMWRiZDc2MmEwOGQ5ZTc3YzA4MDY5ZWQ3Yzk1MzY0YWEzMGNhMTA3MjIwODU2MWI3MzBlOGQ3NSJ9fX0= + HORSE: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYjY2YjJiMzJkMzE1MzljNzM4M2Q5MjNiYWU0ZmFhZjY1ZGE2NzE1Y2Q1MjZjMzVkMmU0ZTY4MjVkYTExZmIifX19 + HUSK: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDY3NGM2M2M4ZGI1ZjRjYTYyOGQ2OWEzYjFmOGEzNmUyOWQ4ZmQ3NzVlMWE2YmRiNmNhYmI0YmU0ZGIxMjEifX19 + ILLUSIONER: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmYyODgyZGQwOTcyM2U0N2MwYWI5NjYzZWFiMDgzZDZhNTk2OTI3MzcwNjExMGM4MjkxMGU2MWJmOGE4ZjA3ZSJ9fX0= + IRON_GOLEM: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvODkwOTFkNzllYTBmNTllZjdlZjk0ZDdiYmE2ZTVmMTdmMmY3ZDQ1NzJjNDRmOTBmNzZjNDgxOWE3MTQifX19 + LLAMA: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvODNkOWI1OTE1OTEyZmZjMmI4NTc2MWQ2YWRjYjQyOGE4MTJmOWI4M2ZmNjM0ZTMzMTE2MmNlNDZjOTllOSJ9fX0= + MAGMA_CUBE: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYTFjOTdhMDZlZmRlMDRkMDAyODdiZjIwNDE2NDA0YWIyMTAzZTEwZjA4NjIzMDg3ZTFiMGMxMjY0YTFjMGYwYyJ9fX0= + MULE: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDZkY2RhMjY1ZTU3ZTRmNTFiMTQ1YWFjYmY1YjU5YmRjNjA5OWZmZDNjY2UwYTY2MWIyYzAwNjVkODA5MzBkOCJ9fX0= + MUSHROOM_COW: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmI1Mjg0MWYyZmQ1ODllMGJjODRjYmFiZjllMWMyN2NiNzBjYWM5OGY4ZDZiM2RkMDY1ZTU1YTRkY2I3MGQ3NyJ9fX0= + OCELOT: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNTY1N2NkNWMyOTg5ZmY5NzU3MGZlYzRkZGNkYzY5MjZhNjhhMzM5MzI1MGMxYmUxZjBiMTE0YTFkYjEifX19 + PANDA: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDE4OGM5ODBhYWNmYTk0Y2YzMzA4ODUxMmIxYjk1MTdiYTgyNmIxNTRkNGNhZmMyNjJhZmY2OTc3YmU4YSJ9fX0= + PARROT: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZjBiZmE4NTBmNWRlNGIyOTgxY2NlNzhmNTJmYzJjYzdjZDdiNWM2MmNhZWZlZGRlYjljZjMxMWU4M2Q5MDk3In19fQ== + PHANTOM: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDExZDI1YmNkYWJhZmFkNWZkNmUwMTBjNWIxY2Y3YTAwYzljY2E0MGM1YTQ2NzQ3ZjcwNmRjOWNiM2EifX19 + PIG: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMWEzNzFhMDZlYTc4NThmODlkMjdjYzEwNTVjMzE3YjIzZjEwNWM5MTI1YmM1MTZkMzg5MWFhNGM4MzVjMjk5In19fQ== + PIGLIN: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvY2NlZDlkODAxYWE2ZjgzZjhlNDlmOTBkOWE4Yjg1YjdmOGZkYTU4M2Q4NWY3MmNmZmI2OTg2NzI1Nzg5ZjYzNiJ9fX0= + PIGLIN_BRUTE: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvY2NlZDlkODAxYWE2ZjgzZjhlNDlmOTBkOWE4Yjg1YjdmOGZkYTU4M2Q4NWY3MmNmZmI2OTg2NzI1Nzg5ZjYzNiJ9fX0= + PILLAGER: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNGFlZTZiYjM3Y2JmYzkyYjBkODZkYjVhZGE0NzkwYzY0ZmY0NDY4ZDY4Yjg0OTQyZmRlMDQ0MDVlOGVmNTMzMyJ9fX0= + POLAR_BEAR: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYzRmZTkyNjkyMmZiYjQwNmYzNDNiMzRhMTBiYjk4OTkyY2VlNDQxMDEzN2QzZjg4MDk5NDI3YjIyZGUzYWI5MCJ9fX0= + PUFFERFISH: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMjkyMzUwYzlmMDk5M2VkNTRkYjJjNzExMzkzNjMyNTY4M2ZmYzIwMTA0YTliNjIyYWE0NTdkMzdlNzA4ZDkzMSJ9fX0= + RABBIT: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYzk3N2EzMjY2YmYzYjllYWYxN2U1YTAyZWE1ZmJiNDY4MDExNTk4NjNkZDI4OGI5M2U2YzEyYzljYiJ9fX0= + RAVAGER: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvY2QyMGJmNTJlYzM5MGEwNzk5Mjk5MTg0ZmM2NzhiZjg0Y2Y3MzJiYjFiZDc4ZmQxYzRiNDQxODU4ZjAyMzVhOCJ9fX0= + SALMON: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMjBlYTlhMjIzNjIwY2RiNTRiMzU3NDEzZDQzYmQ4OWM0MDA4YmNhNmEyMjdmM2I3ZGI5N2Y3NzMzZWFkNWZjZiJ9fX0= + SHEEP: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZjMxZjljY2M2YjNlMzJlY2YxM2I4YTExYWMyOWNkMzNkMThjOTVmYzczZGI4YTY2YzVkNjU3Y2NiOGJlNzAifX19 + SILVERFISH: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZGE5MWRhYjgzOTFhZjVmZGE1NGFjZDJjMGIxOGZiZDgxOWI4NjVlMWE4ZjFkNjIzODEzZmE3NjFlOTI0NTQwIn19fQ== + SLIME: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvODk1YWVlYzZiODQyYWRhODY2OWY4NDZkNjViYzQ5NzYyNTk3ODI0YWI5NDRmMjJmNDViZjNiYmI5NDFhYmU2YyJ9fX0= + SNOWMAN: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvOGU4ZDIwNmY2MWU2ZGU4YTc5ZDBjYjBiY2Q5OGFjZWQ0NjRjYmZlZmM5MjFiNDE2MGEyNTI4MjE2MzExMmEifX19 + SPIDER: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvY2Q1NDE1NDFkYWFmZjUwODk2Y2QyNThiZGJkZDRjZjgwYzNiYTgxNjczNTcyNjA3OGJmZTM5MzkyN2U1N2YxIn19fQ== + SQUID: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDljMmM5Y2U2N2ViNTk3MWNjNTk1ODQ2M2U2YzlhYmFiOGU1OTlhZGMyOTVmNGQ0MjQ5OTM2YjAwOTU3NjlkZCJ9fX0= + STRAY: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMmM1MDk3OTE2YmMwNTY1ZDMwNjAxYzBlZWJmZWIyODcyNzdhMzRlODY3YjRlYTQzYzYzODE5ZDUzZTg5ZWRlNyJ9fX0= + STRIDER: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvY2I3ZmZkZGE2NTZjNjhkODg4NTFhOGUwNWI0OGNkMjQ5Mzc3M2ZmYzRhYjdkNjRlOTMwMjIyOWZlMzU3MTA1OSJ9fX0= + TRADER_LLAMA: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvODQyNDc4MGIzYzVjNTM1MWNmNDlmYjViZjQxZmNiMjg5NDkxZGY2YzQzMDY4M2M4NGQ3ODQ2MTg4ZGI0Zjg0ZCJ9fX0= + TROPICAL_FISH: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDZkZDVlNmFkZGI1NmFjYmM2OTRlYTRiYTU5MjNiMWIyNTY4ODE3OGZlZmZhNzIyOTAyOTllMjUwNWM5NzI4MSJ9fX0= + TURTLE: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMGE0MDUwZTdhYWNjNDUzOTIwMjY1OGZkYzMzOWRkMTgyZDdlMzIyZjlmYmNjNGQ1Zjk5YjU3MThhIn19fQ== + VEX: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYzJlYzVhNTE2NjE3ZmYxNTczY2QyZjlkNWYzOTY5ZjU2ZDU1NzVjNGZmNGVmZWZhYmQyYTE4ZGM3YWI5OGNkIn19fQ== + VILLAGER: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDFiODMwZWI0MDgyYWNlYzgzNmJjODM1ZTQwYTExMjgyYmI1MTE5MzMxNWY5MTE4NDMzN2U4ZDM1NTU1ODMifX19 + VINDICATOR: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNmRlYWVjMzQ0YWIwOTViNDhjZWFkNzUyN2Y3ZGVlNjFiMDYzZmY3OTFmNzZhOGZhNzY2NDJjODY3NmUyMTczIn19fQ== + WANDERING_TRADER: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNWYxMzc5YTgyMjkwZDdhYmUxZWZhYWJiYzcwNzEwZmYyZWMwMmRkMzRhZGUzODZiYzAwYzkzMGM0NjFjZjkzMiJ9fX0= + WITCH: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvMjBlMTNkMTg0NzRmYzk0ZWQ1NWFlYjcwNjk1NjZlNDY4N2Q3NzNkYWMxNmY0YzNmODcyMmZjOTViZjlmMmRmYSJ9fX0= + WOLF: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDA0OThkZTZmNWIwOWUwY2UzNWE3MjkyZmU1MGI3OWZjZTkwNjVkOWJlOGUyYTg3YzdhMTM1NjZlZmIyNmQ3MiJ9fX0= + ZOGLIN: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZTY3ZTE4NjAyZTAzMDM1YWQ2ODk2N2NlMDkwMjM1ZDg5OTY2NjNmYjllYTQ3NTc4ZDNhN2ViYmM0MmE1Y2NmOSJ9fX0= + ZOMBIFIED_PIGLIN: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvN2VhYmFlY2M1ZmFlNWE4YTQ5Yzg4NjNmZjQ4MzFhYWEyODQxOThmMWEyMzk4ODkwYzc2NWUwYThkZTE4ZGE4YyJ9fX0= + ZOMBIE_HORSE: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZDIyOTUwZjJkM2VmZGRiMThkZTg2ZjhmNTVhYzUxOGRjZTczZjEyYTZlMGY4NjM2ZDU1MWQ4ZWI0ODBjZWVjIn19fQ== + ZOMBIE_VILLAGER: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZTVlMDhhODc3NmMxNzY0YzNmZTZhNmRkZDQxMmRmY2I4N2Y0MTMzMWRhZDQ3OWFjOTZjMjFkZjRiZjNhYzg5YyJ9fX0= + SKELETON_HORSE: eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNDdlZmZjZTM1MTMyYzg2ZmY3MmJjYWU3N2RmYmIxZDIyNTg3ZTk0ZGYzY2JjMjU3MGVkMTdjZjg5NzNhIn19fQ== diff --git a/Core/src/main/resources/enchants/divine_touch.yml b/Core/src/main/resources/enchants/divine_touch.yml new file mode 100644 index 0000000..281457c --- /dev/null +++ b/Core/src/main/resources/enchants/divine_touch.yml @@ -0,0 +1,33 @@ +Is_Treasure: false +Name: Divine Touch +Tier: legendary +Description: + - '%enchantment_trigger_chance%% chance to obtain &fMob Spawner&8.' +Level: + Min: 1 + Max: 5 +Anvil: + Merge_Cost: '%enchantment_level%' +Fishing: + Chance: 5.0 +Enchanting_Table: + Level_By_Exp_Cost: '6.0 * %enchantment_level%' + Chance: 5.0 +Villagers: + Chance: 5.0 +Loot_Generation: + Chance: 8.0 +Mob_Spawning: + Chance: 0.0 +Settings: + Trigger_Chance: '15.0 * %enchantment_level%' + Cost: + Enabled: false + Item: + Material: AIR + Amount: 1 + Particle: + Name: 'VILLAGER_HAPPY' + Data: '' + Spawner_Item: + Name: '&aMob Spawner &7(%type%)' diff --git a/Core/src/main/resources/enchants/double_strike.yml b/Core/src/main/resources/enchants/double_strike.yml new file mode 100644 index 0000000..78ca809 --- /dev/null +++ b/Core/src/main/resources/enchants/double_strike.yml @@ -0,0 +1,32 @@ +Is_Treasure: false +Name: Double Strike +Tier: legendary +Description: + - '%enchantment_trigger_chance%% chance to inflict double damage.' +Level: + Min: 1 + Max: 4 +Anvil: + Merge_Cost: '%enchantment_level%' +Fishing: + Chance: 10.0 +Enchanting_Table: + Level_By_Exp_Cost: '7 * %enchantment_level%' + Chance: 10.0 +Villagers: + Chance: 10.0 +Loot_Generation: + Chance: 15.0 +Mob_Spawning: + Chance: 10.0 +Settings: + Trigger_Chance: '4.0 + %enchantment_level% * 0.8' + Cost: + Enabled: false + Item: + Material: AIR + Amount: 1 + Particle: + Name: 'EXPLOSION_NORMAL' + Data: '' + Sound: 'ENTITY_GENERIC_EXPLODE' diff --git a/Core/src/main/resources/enchants/ender_bow.yml b/Core/src/main/resources/enchants/ender_bow.yml new file mode 100644 index 0000000..a461d67 --- /dev/null +++ b/Core/src/main/resources/enchants/ender_bow.yml @@ -0,0 +1,28 @@ +Is_Treasure: false +Name: Ender Bow +Tier: legendary +Description: + - '%enchantment_trigger_chance%% chance to shoot Ender Pearl instead of arrows.' +Level: + Min: 1 + Max: 1 +Anvil: + Merge_Cost: '%enchantment_level%' +Fishing: + Chance: 5.0 +Enchanting_Table: + Level_By_Exp_Cost: '30' + Chance: 5.0 +Villagers: + Chance: 3.0 +Loot_Generation: + Chance: 10.0 +Mob_Spawning: + Chance: 0.0 +Settings: + Trigger_Chance: '100.0' + Cost: + Enabled: false + Item: + Material: ENDER_PEARL + Amount: 1 diff --git a/Core/src/main/resources/enchants/exhaust.yml b/Core/src/main/resources/enchants/exhaust.yml new file mode 100644 index 0000000..48270e9 --- /dev/null +++ b/Core/src/main/resources/enchants/exhaust.yml @@ -0,0 +1,34 @@ +Is_Treasure: false +Name: Exhaust +Tier: common +Description: + - '%enchantment_trigger_chance%% chance to apply %enchantment_potion_type% %enchantment_potion_level% for %enchantment_potion_duration%s. on hit.' +Level: + Min: 1 + Max: 3 +Anvil: + Merge_Cost: '%enchantment_level%' +Fishing: + Chance: 70.0 +Enchanting_Table: + Level_By_Exp_Cost: '9 * %enchantment_level%' + Chance: 70.0 +Villagers: + Chance: 70.0 +Loot_Generation: + Chance: 70.0 +Mob_Spawning: + Chance: 50.0 +Settings: + Trigger_Chance: '20.0 * %enchantment_level%' + Cost: + Enabled: false + Item: + Material: AIR + Amount: 1 + Potion_Effect: + Duration: '3.0 + %enchantment_level% * 1.5' + Level: '%enchantment_level%' + Particle: + Name: '' + Data: '' diff --git a/Core/src/main/resources/enchants/exp_hunter.yml b/Core/src/main/resources/enchants/exp_hunter.yml new file mode 100644 index 0000000..4db663e --- /dev/null +++ b/Core/src/main/resources/enchants/exp_hunter.yml @@ -0,0 +1,29 @@ +Is_Treasure: false +Name: Exp Hunter +Tier: rare +Description: + - '%enchantment_trigger_chance%% chance to obtain +%enchantment_exp_modifier%% more exp from mobs.' +Level: + Min: 1 + Max: 5 +Anvil: + Merge_Cost: '%enchantment_level%' +Fishing: + Chance: 45.0 +Enchanting_Table: + Level_By_Exp_Cost: '6 * %enchantment_level%' + Chance: 45.0 +Villagers: + Chance: 45.0 +Loot_Generation: + Chance: 45.0 +Mob_Spawning: + Chance: 0.0 +Settings: + Trigger_Chance: '50.0 + %enchantment_level% * 5' + Cost: + Enabled: false + Item: + Material: AIR + Amount: 1 + Exp_Modifier: '1.0 + %enchantment_level% * 0.5' diff --git a/Core/src/main/resources/enchants/explosive_arrows.yml b/Core/src/main/resources/enchants/explosive_arrows.yml new file mode 100644 index 0000000..fbd95e2 --- /dev/null +++ b/Core/src/main/resources/enchants/explosive_arrows.yml @@ -0,0 +1,37 @@ +Is_Treasure: false +Name: Explosive Arrows +Tier: exotic +Description: + - '%enchantment_trigger_chance%% chance to launch an explosive arrow with x%enchantment_explosion_power% power.' +Level: + Min: 1 + Max: 3 +Anvil: + Merge_Cost: '%enchantment_level%' +Fishing: + Chance: 40.0 +Enchanting_Table: + Level_By_Exp_Cost: '9 * %enchantment_level%' + Chance: 40.0 +Villagers: + Chance: 40.0 +Loot_Generation: + Chance: 50.0 +Mob_Spawning: + Chance: 25.0 +Settings: + Trigger_Chance: '10.0 + %enchantment_level% * 5' + Cost: + Enabled: false + Item: + Material: AIR + Amount: 1 + Explosion: + Fire_Spread: true + Damage_Items: true + Damage_Blocks: false + Size: '2.0 + %enchantment_level%' + Arrow: + Trail: + Name: 'SMOKE_NORMAL' + Data: '' diff --git a/Core/src/main/resources/enchants/flame_walker.yml b/Core/src/main/resources/enchants/flame_walker.yml new file mode 100644 index 0000000..5c7a9dd --- /dev/null +++ b/Core/src/main/resources/enchants/flame_walker.yml @@ -0,0 +1,29 @@ +Is_Treasure: false +Name: Flame Walker +Tier: exotic +Description: + - 'Ability to walk on lava and on magma blocks without getting damage.' +Level: + Min: 1 + Max: 3 +Anvil: + Merge_Cost: '%enchantment_level%' +Fishing: + Chance: 40.0 +Enchanting_Table: + Level_By_Exp_Cost: '9 * %enchantment_level%' + Chance: 40.0 +Villagers: + Chance: 40.0 +Loot_Generation: + Chance: 50.0 +Mob_Spawning: + Chance: 0.0 +Settings: + Trigger_Chance: '100.0' + Cost: + Enabled: false + Item: + Material: AIR + Amount: 1 + Block_Decay: '5.0 + %enchantment_level% * 2' diff --git a/Core/src/main/resources/enchants/ghast.yml b/Core/src/main/resources/enchants/ghast.yml new file mode 100644 index 0000000..f9f2766 --- /dev/null +++ b/Core/src/main/resources/enchants/ghast.yml @@ -0,0 +1,30 @@ +Is_Treasure: false +Name: Ghast +Tier: rare +Description: + - '%enchantment_trigger_chance%% chance to launch a fireball instead of arrow.' +Level: + Min: 1 + Max: 3 +Anvil: + Merge_Cost: '%enchantment_level%' +Fishing: + Chance: 40.0 +Enchanting_Table: + Level_By_Exp_Cost: '9 * %enchantment_level%' + Chance: 40.0 +Villagers: + Chance: 40.0 +Loot_Generation: + Chance: 40.0 +Mob_Spawning: + Chance: 20.0 +Settings: + Trigger_Chance: '100.0' + Fire_Spread: true + Yield: '1.0 * %enchantment_level%' + Cost: + Enabled: false + Item: + Material: AIR + Amount: 1 \ No newline at end of file diff --git a/Core/src/main/resources/enchants/hardened.yml b/Core/src/main/resources/enchants/hardened.yml new file mode 100644 index 0000000..3f515e9 --- /dev/null +++ b/Core/src/main/resources/enchants/hardened.yml @@ -0,0 +1,31 @@ +Is_Treasure: false +Name: Hardened +Tier: rare +Description: + - '%enchantment_trigger_chance%% chance to obtain %enchantment_potion_type% %enchantment_potion_level% for %enchantment_potion_duration%s. when damaged.' +Level: + Min: 1 + Max: 3 +Anvil: + Merge_Cost: '%enchantment_level%' +Fishing: + Chance: 30.0 +Enchanting_Table: + Level_By_Exp_Cost: '9 * %enchantment_level%' + Chance: 30.0 +Villagers: + Chance: 30.0 +Loot_Generation: + Chance: 30.0 +Mob_Spawning: + Chance: 20.0 +Settings: + Trigger_Chance: '10.0 + %enchantment_level% * 5' + Cost: + Enabled: false + Item: + Material: AIR + Amount: 1 + Potion_Effect: + Duration: '2.5 + %enchantment_level%' + Level: '%enchantment_level%' diff --git a/Core/src/main/resources/enchants/haste.yml b/Core/src/main/resources/enchants/haste.yml new file mode 100644 index 0000000..076ae8d --- /dev/null +++ b/Core/src/main/resources/enchants/haste.yml @@ -0,0 +1,31 @@ +Is_Treasure: false +Name: Haste +Tier: rare +Description: + - 'Grants %enchantment_potion_type% %enchantment_potion_level% effect.' +Level: + Min: 1 + Max: 3 +Anvil: + Merge_Cost: '%enchantment_level%' +Fishing: + Chance: 40.0 +Enchanting_Table: + Level_By_Exp_Cost: '9 * %enchantment_level%' + Chance: 40.0 +Villagers: + Chance: 40.0 +Loot_Generation: + Chance: 40.0 +Mob_Spawning: + Chance: 0.0 +Settings: + Trigger_Chance: '100.0' + Cost: + Enabled: false + Item: + Material: SUGAR + Amount: 1 + Potion_Effect: + Duration: '25.0' + Level: '%enchantment_level%' diff --git a/Core/src/main/resources/enchants/ice_aspect.yml b/Core/src/main/resources/enchants/ice_aspect.yml new file mode 100644 index 0000000..5ebd743 --- /dev/null +++ b/Core/src/main/resources/enchants/ice_aspect.yml @@ -0,0 +1,34 @@ +Is_Treasure: false +Name: Ice Aspect +Tier: common +Description: + - '%enchantment_trigger_chance%% chance to freeze and apply %enchantment_potion_type% %enchantment_potion_level% for %enchantment_potion_duration%s. on hit.' +Level: + Min: 1 + Max: 3 +Anvil: + Merge_Cost: '%enchantment_level%' +Fishing: + Chance: 70.0 +Enchanting_Table: + Level_By_Exp_Cost: '9 * %enchantment_level%' + Chance: 70.0 +Villagers: + Chance: 70.0 +Loot_Generation: + Chance: 70.0 +Mob_Spawning: + Chance: 50.0 +Settings: + Trigger_Chance: '25.0 * %enchantment_level%' + Cost: + Enabled: false + Item: + Material: AIR + Amount: 1 + Potion_Effect: + Duration: '3.0 + %enchantment_level%' + Level: '%enchantment_level%' + Particle: + Name: 'BLOCK_CRACK' + Data: 'ICE' diff --git a/Core/src/main/resources/enchants/infernus.yml b/Core/src/main/resources/enchants/infernus.yml new file mode 100644 index 0000000..f4f2610 --- /dev/null +++ b/Core/src/main/resources/enchants/infernus.yml @@ -0,0 +1,29 @@ +Is_Treasure: false +Name: Infernus +Tier: common +Description: + - '%enchantment_trigger_chance%% chance to launch flaming Trident that ignites the enemy for %enchantment_fire_duration%s.' +Level: + Min: 1 + Max: 3 +Anvil: + Merge_Cost: '%enchantment_level%' +Fishing: + Chance: 45.0 +Enchanting_Table: + Level_By_Exp_Cost: '9 * %enchantment_level%' + Chance: 35.0 +Villagers: + Chance: 45.0 +Loot_Generation: + Chance: 45.0 +Mob_Spawning: + Chance: 30.0 +Settings: + Trigger_Chance: '100.0' + Cost: + Enabled: false + Item: + Material: AIR + Amount: 1 + Fire_Ticks: '60 + %enchantment_level% * 20' diff --git a/Core/src/main/resources/enchants/lucky_miner.yml b/Core/src/main/resources/enchants/lucky_miner.yml new file mode 100644 index 0000000..1108e9b --- /dev/null +++ b/Core/src/main/resources/enchants/lucky_miner.yml @@ -0,0 +1,29 @@ +Is_Treasure: false +Name: Lucky Miner +Tier: common +Description: + - '%enchantment_trigger_chance%% chance to obtain +%enchantment_exp_modifier%% more exp from ores.' +Level: + Min: 1 + Max: 5 +Anvil: + Merge_Cost: '%enchantment_level%' +Fishing: + Chance: 50.0 +Enchanting_Table: + Level_By_Exp_Cost: '6 * %enchantment_level%' + Chance: 50.0 +Villagers: + Chance: 50.0 +Loot_Generation: + Chance: 60.0 +Mob_Spawning: + Chance: 0.0 +Settings: + Trigger_Chance: '30.0 + %enchantment_level% * 7.0' + Cost: + Enabled: false + Item: + Material: AIR + Amount: 1 + Exp_Modifier: '1.0 + %enchantment_level% * 0.5' diff --git a/Core/src/main/resources/enchants/night_vision.yml b/Core/src/main/resources/enchants/night_vision.yml new file mode 100644 index 0000000..983b628 --- /dev/null +++ b/Core/src/main/resources/enchants/night_vision.yml @@ -0,0 +1,31 @@ +Is_Treasure: false +Name: Night Vision +Tier: rare +Description: + - 'Grants %enchantment_potion_type% %enchantment_potion_level% effect that costs x1 %enchantment_cost_item%.' +Level: + Min: 1 + Max: 1 +Anvil: + Merge_Cost: '%enchantment_level%' +Fishing: + Chance: 20.0 +Enchanting_Table: + Level_By_Exp_Cost: '15' + Chance: 20.0 +Villagers: + Chance: 20.0 +Loot_Generation: + Chance: 20.0 +Mob_Spawning: + Chance: 0.0 +Settings: + Trigger_Chance: '100.0' + Cost: + Enabled: true + Item: + Material: GLOWSTONE_DUST + Amount: 1 + Potion_Effect: + Duration: '60.0' + Level: '1' diff --git a/Core/src/main/resources/enchants/nimble.yml b/Core/src/main/resources/enchants/nimble.yml new file mode 100644 index 0000000..247f80a --- /dev/null +++ b/Core/src/main/resources/enchants/nimble.yml @@ -0,0 +1,28 @@ +Is_Treasure: false +Name: Nimble +Tier: rare +Description: + - 'Moves all mob''s loot directly to your inventory.' +Level: + Min: 1 + Max: 1 +Anvil: + Merge_Cost: '%enchantment_level%' +Fishing: + Chance: 40.0 +Enchanting_Table: + Level_By_Exp_Cost: '15 * %enchantment_level%' + Chance: 40.0 +Villagers: + Chance: 40.0 +Loot_Generation: + Chance: 40.0 +Mob_Spawning: + Chance: 0.0 +Settings: + Trigger_Chance: '100.0' + Cost: + Enabled: false + Item: + Material: AIR + Amount: 1 \ No newline at end of file diff --git a/Core/src/main/resources/enchants/paralyze.yml b/Core/src/main/resources/enchants/paralyze.yml new file mode 100644 index 0000000..bba51b5 --- /dev/null +++ b/Core/src/main/resources/enchants/paralyze.yml @@ -0,0 +1,34 @@ +Is_Treasure: false +Name: Paralyze +Tier: rare +Description: + - '%enchantment_trigger_chance%% chance to apply %enchantment_potion_type% %enchantment_potion_level% for %enchantment_potion_duration%s. on hit.' +Level: + Min: 1 + Max: 3 +Anvil: + Merge_Cost: '%enchantment_level%' +Fishing: + Chance: 50.0 +Enchanting_Table: + Level_By_Exp_Cost: '9 * %enchantment_level%' + Chance: 50.0 +Villagers: + Chance: 50.0 +Loot_Generation: + Chance: 50.0 +Mob_Spawning: + Chance: 25.0 +Settings: + Trigger_Chance: '10.0 * %enchantment_level%' + Cost: + Enabled: false + Item: + Material: AIR + Amount: 1 + Potion_Effect: + Duration: '2.5 + %enchantment_level% * 0.5' + Level: '%enchantment_level%' + Particle: + Name: 'CRIT_MAGIC' + Data: '' diff --git a/Core/src/main/resources/enchants/poisoned_arrows.yml b/Core/src/main/resources/enchants/poisoned_arrows.yml new file mode 100644 index 0000000..950bf77 --- /dev/null +++ b/Core/src/main/resources/enchants/poisoned_arrows.yml @@ -0,0 +1,31 @@ +Is_Treasure: false +Name: Poisoned Arrows +Tier: common +Description: + - '%enchantment_trigger_chance%% chance to launch an arrow with %enchantment_potion_type% %enchantment_potion_level% (%enchantment_potion_duration%s.)' +Level: + Min: 1 + Max: 3 +Anvil: + Merge_Cost: '%enchantment_level%' +Fishing: + Chance: 50.0 +Enchanting_Table: + Level_By_Exp_Cost: '9 * %enchantment_level%' + Chance: 50.0 +Villagers: + Chance: 50.0 +Loot_Generation: + Chance: 50.0 +Mob_Spawning: + Chance: 30.0 +Settings: + Trigger_Chance: '25.0 + %enchantment_level% * 5' + Cost: + Enabled: false + Item: + Material: AIR + Amount: 1 + Potion_Effect: + Duration: '2.0 + %enchantment_level%' + Level: '%enchantment_level%' \ No newline at end of file diff --git a/Core/src/main/resources/enchants/rage.yml b/Core/src/main/resources/enchants/rage.yml new file mode 100644 index 0000000..f5228ff --- /dev/null +++ b/Core/src/main/resources/enchants/rage.yml @@ -0,0 +1,34 @@ +Is_Treasure: false +Name: Rage +Tier: rare +Description: + - '%enchantment_trigger_chance%% chance to get %enchantment_potion_type% %enchantment_potion_level% effect for %enchantment_potion_duration%s. on hit.' +Level: + Min: 1 + Max: 3 +Anvil: + Merge_Cost: '%enchantment_level%' +Fishing: + Chance: 30.0 +Enchanting_Table: + Level_By_Exp_Cost: '9 * %enchantment_level%' + Chance: 30.0 +Villagers: + Chance: 30.0 +Loot_Generation: + Chance: 30.0 +Mob_Spawning: + Chance: 15.0 +Settings: + Trigger_Chance: '7.0 + %enchantment_level%' + Cost: + Enabled: false + Item: + Material: AIR + Amount: 1 + Potion_Effect: + Duration: '3.0 + %enchantment_level% * 0.5' + Level: '%enchantment_level%' + Particle: + Name: 'LAVA' + Data: '' diff --git a/Core/src/main/resources/enchants/regrowth.yml b/Core/src/main/resources/enchants/regrowth.yml new file mode 100644 index 0000000..4736309 --- /dev/null +++ b/Core/src/main/resources/enchants/regrowth.yml @@ -0,0 +1,34 @@ +Is_Treasure: false +Name: Regrowth +Tier: exotic +Description: + - 'Restores %enchantment_health_amount% hearts every %enchantment_health_interval%s. with %enchantment_trigger_chance%% chance.' +Level: + Min: 1 + Max: 5 +Anvil: + Merge_Cost: '%enchantment_level%' +Fishing: + Chance: 15.0 +Enchanting_Table: + Level_By_Exp_Cost: '6 * %enchantment_level%' + Chance: 10.0 +Villagers: + Chance: 15.0 +Loot_Generation: + Chance: 15.0 +Mob_Spawning: + Chance: 10.0 +Settings: + Trigger_Chance: '20.0 + %enchantment_level% * 5' + Cost: + Enabled: false + Item: + Material: AIR + Amount: 1 + Particle: + Name: 'HEART' + Data: '' + Health: + Amount: '%enchantment_level% * 0.25' + Interval: 100 \ No newline at end of file diff --git a/Core/src/main/resources/enchants/replanter.yml b/Core/src/main/resources/enchants/replanter.yml new file mode 100644 index 0000000..9f87016 --- /dev/null +++ b/Core/src/main/resources/enchants/replanter.yml @@ -0,0 +1,31 @@ +Is_Treasure: false +Name: Replanter +Tier: common +Description: + - 'Automatically replant crops on right click and harvest.' +Level: + Min: 1 + Max: 1 +Anvil: + Merge_Cost: '%enchantment_level%' +Fishing: + Chance: 50.0 +Enchanting_Table: + Level_By_Exp_Cost: '15' + Chance: 50.0 +Villagers: + Chance: 50.0 +Loot_Generation: + Chance: 50.0 +Mob_Spawning: + Chance: 0.0 +Settings: + Trigger_Chance: '100.0' + Cost: + Enabled: false + Item: + Material: AIR + Amount: 1 + Replant: + On_Right_Click: true + On_Plant_Break: true diff --git a/Core/src/main/resources/enchants/rocket.yml b/Core/src/main/resources/enchants/rocket.yml new file mode 100644 index 0000000..ef46c87 --- /dev/null +++ b/Core/src/main/resources/enchants/rocket.yml @@ -0,0 +1,29 @@ +Is_Treasure: false +Name: Rocket +Tier: exotic +Description: + - '%enchantment_trigger_chance%% chance to launch your enemy into the space.' +Level: + Min: 1 + Max: 3 +Anvil: + Merge_Cost: '%enchantment_level%' +Fishing: + Chance: 50.0 +Enchanting_Table: + Level_By_Exp_Cost: '9 * %enchantment_level%' + Chance: 50.0 +Villagers: + Chance: 50.0 +Loot_Generation: + Chance: 50.0 +Mob_Spawning: + Chance: 20.0 +Settings: + Trigger_Chance: '4.0 + %enchantment_level%' + Cost: + Enabled: false + Item: + Material: AIR + Amount: 1 + Firework_Power: '%enchantment_level%' diff --git a/Core/src/main/resources/enchants/saturation.yml b/Core/src/main/resources/enchants/saturation.yml new file mode 100644 index 0000000..d212924 --- /dev/null +++ b/Core/src/main/resources/enchants/saturation.yml @@ -0,0 +1,31 @@ +Is_Treasure: false +Name: Saturation +Tier: rare +Description: + - 'Restores %enchantment_saturation_amount% food points every %enchantment_saturation_interval%s. with %enchantment_trigger_chance%% chance.' +Level: + Min: 1 + Max: 3 +Anvil: + Merge_Cost: '%enchantment_level%' +Fishing: + Chance: 20.0 +Enchanting_Table: + Level_By_Exp_Cost: '9 * %enchantment_level%' + Chance: 20.0 +Villagers: + Chance: 25.0 +Loot_Generation: + Chance: 25.0 +Mob_Spawning: + Chance: 0.0 +Settings: + Trigger_Chance: '25.0 + %enchantment_level% * 5' + Cost: + Enabled: false + Item: + Material: AIR + Amount: 1 + Saturation: + Amount: '%enchantment_level%' + Interval: 150 diff --git a/Core/src/main/resources/enchants/scavenger.yml b/Core/src/main/resources/enchants/scavenger.yml new file mode 100644 index 0000000..a116f83 --- /dev/null +++ b/Core/src/main/resources/enchants/scavenger.yml @@ -0,0 +1,37 @@ +Is_Treasure: false +Name: Scavenger +Tier: common +Description: + - '%enchantment_trigger_chance%% chance to obtain additional loot from mobs.' +Level: + Min: 1 + Max: 4 +Anvil: + Merge_Cost: '%enchantment_level%' +Fishing: + Chance: 60.0 +Enchanting_Table: + Level_By_Exp_Cost: '7 * %enchantment_level%' + Chance: 60.0 +Villagers: + Chance: 70.0 +Loot_Generation: + Chance: 70.0 +Mob_Spawning: + Chance: 0.0 +Settings: + Trigger_Chance: '15.0 + %enchantment_level% * 10' + Cost: + Enabled: false + Item: + Material: AIR + Amount: 1 + Treasures: + VILLAGER: + EMERALD: + Amount: '1:1' + Chance: 5.0 + SKELETON: + BONE_MEAL: + Amount: '1:2' + Chance: 50.0 \ No newline at end of file diff --git a/Core/src/main/resources/enchants/self_destruction.yml b/Core/src/main/resources/enchants/self_destruction.yml new file mode 100644 index 0000000..a5a6579 --- /dev/null +++ b/Core/src/main/resources/enchants/self_destruction.yml @@ -0,0 +1,30 @@ +Is_Treasure: false +Name: Self-Destruction +Tier: exotic +Description: + - '%enchantment_trigger_chance%% chance to create an x%enchantment_explosion_power% power explosion on death.' +Level: + Min: 1 + Max: 3 +Anvil: + Merge_Cost: '%enchantment_level%' +Fishing: + Chance: 20.0 +Enchanting_Table: + Level_By_Exp_Cost: '9 * %enchantment_level%' + Chance: 20.0 +Villagers: + Chance: 20.0 +Loot_Generation: + Chance: 20.0 +Mob_Spawning: + Chance: 15.0 +Settings: + Trigger_Chance: '20.0 + %enchantment_level% * 10' + Cost: + Enabled: false + Item: + Material: AIR + Amount: 1 + Explosion: + Size: '1.0 + %enchantment_level%' diff --git a/Core/src/main/resources/enchants/silk_chest.yml b/Core/src/main/resources/enchants/silk_chest.yml new file mode 100644 index 0000000..cab74c3 --- /dev/null +++ b/Core/src/main/resources/enchants/silk_chest.yml @@ -0,0 +1,30 @@ +Is_Treasure: false +Name: Silk Chest +Tier: rare +Description: + - 'Drop chests and saves all its content.' +Level: + Min: 1 + Max: 1 +Anvil: + Merge_Cost: '%enchantment_level%' +Fishing: + Chance: 30.0 +Enchanting_Table: + Level_By_Exp_Cost: '30' + Chance: 30.0 +Villagers: + Chance: 30.0 +Loot_Generation: + Chance: 30.0 +Mob_Spawning: + Chance: 0.0 +Settings: + Trigger_Chance: '100.0' + Cost: + Enabled: false + Item: + Material: AIR + Amount: 1 + Chest_Item: + Name: '&f%name% &7(%items% items)' diff --git a/Core/src/main/resources/enchants/smelter.yml b/Core/src/main/resources/enchants/smelter.yml new file mode 100644 index 0000000..0d646f6 --- /dev/null +++ b/Core/src/main/resources/enchants/smelter.yml @@ -0,0 +1,38 @@ +Is_Treasure: false +Name: Smelter +Tier: rare +Description: + - '%enchantment_trigger_chance%% chance to smelt ore or a block on mining.' +Level: + Min: 1 + Max: 5 +Anvil: + Merge_Cost: '%enchantment_level%' +Fishing: + Chance: 60.0 +Enchanting_Table: + Level_By_Exp_Cost: '5 * %enchantment_level%' + Chance: 60.0 +Villagers: + Chance: 60.0 +Loot_Generation: + Chance: 60.0 +Mob_Spawning: + Chance: 60.0 +Settings: + Trigger_Chance: '25.0 + %enchantment_level% * 10' + Particle: + Name: 'FLAME' + Data: '' + Cost: + Enabled: false + Item: + Material: AIR + Amount: 1 + Smelting_Table: + IRON_ORE: IRON_INGOT + RAW_IRON: IRON_INGOT + GOLD_ORE: GOLD_INGOT + RAW_GOLD: GOLD_INGOT + RAW_COPPER: COPPER_INGOT + SAND: GLASS \ No newline at end of file diff --git a/Core/src/main/resources/enchants/sonic.yml b/Core/src/main/resources/enchants/sonic.yml new file mode 100644 index 0000000..73ebc7f --- /dev/null +++ b/Core/src/main/resources/enchants/sonic.yml @@ -0,0 +1,30 @@ +Is_Treasure: false +Name: Sonic +Tier: rare +Description: + - 'Grants %enchantment_potion_type% %enchantment_potion_level% effect.' +Level: + Min: 1 + Max: 3 +Anvil: + Merge_Cost: '%enchantment_level%' +Fishing: + Chance: 30.0 +Enchanting_Table: + Level_By_Exp_Cost: '9 * %enchantment_level%' + Chance: 30.0 +Villagers: + Chance: 30.0 +Loot_Generation: + Chance: 40.0 +Mob_Spawning: + Chance: 20.0 +Settings: + Trigger_Chance: '100.0' + Cost: + Enabled: false + Item: + Material: FEATHER + Potion_Effect: + Duration: '25.0' + Level: '%enchantment_level%' diff --git a/Core/src/main/resources/enchants/surprise.yml b/Core/src/main/resources/enchants/surprise.yml new file mode 100644 index 0000000..b606b24 --- /dev/null +++ b/Core/src/main/resources/enchants/surprise.yml @@ -0,0 +1,34 @@ +Is_Treasure: false +Name: Surprise +Tier: exotic +Description: + - '%enchantment_trigger_chance%% chance to apply random potion effect to enemy on hit.' +Level: + Min: 1 + Max: 3 +Anvil: + Merge_Cost: '%enchantment_level%' +Fishing: + Chance: 50.0 +Enchanting_Table: + Level_By_Exp_Cost: '9 * %enchantment_level%' + Chance: 50.0 +Villagers: + Chance: 50.0 +Loot_Generation: + Chance: 50.0 +Mob_Spawning: + Chance: 40.0 +Settings: + Trigger_Chance: '2.25 * %enchantment_level%' + Cost: + Enabled: false + Item: + Material: AIR + Amount: 1 + Potion_Effect: + Duration: '3.0 + %enchantment_level%' + Level: '%enchantment_level%' + Particle: + Name: 'SPELL_WITCH' + Data: '' diff --git a/Core/src/main/resources/enchants/telekinesis.yml b/Core/src/main/resources/enchants/telekinesis.yml new file mode 100644 index 0000000..902a65c --- /dev/null +++ b/Core/src/main/resources/enchants/telekinesis.yml @@ -0,0 +1,32 @@ +Is_Treasure: false +Name: Telekinesis +Tier: exotic +Description: + - 'Moves all blocks loot directly to your inventory.' +Level: + Min: 1 + Max: 3 +Anvil: + Merge_Cost: '%enchantment_level%' +Fishing: + Chance: 20.0 +Enchanting_Table: + Level_By_Exp_Cost: '9 * %enchantment_level%' + Chance: 20.0 +Villagers: + Chance: 30.0 +Loot_Generation: + Chance: 30.0 +Mob_Spawning: + Chance: 0.0 +Settings: + Trigger_Chance: '100.0' + Cost: + Enabled: false + Item: + Material: AIR + Amount: 1 + Message: + Drop_Received: '{message: ~type: ACTION_BAR; ~prefix: false;}%items%' + Item_Name: '&7x%item_amount% &f%item_name%' + Item_Separator: '&7, ' diff --git a/Core/src/main/resources/enchants/thrifty.yml b/Core/src/main/resources/enchants/thrifty.yml new file mode 100644 index 0000000..0e449bd --- /dev/null +++ b/Core/src/main/resources/enchants/thrifty.yml @@ -0,0 +1,35 @@ +Is_Treasure: false +Name: Thrifty +Tier: exotic +Description: + - '%enchantment_trigger_chance%% chance to obtain mob Spawn Egg on kill.' +Level: + Min: 1 + Max: 3 +Anvil: + Merge_Cost: '%enchantment_level%' +Fishing: + Chance: 15.0 +Enchanting_Table: + Level_By_Exp_Cost: '9 * %enchantment_level%' + Chance: 15.0 +Villagers: + Chance: 10.0 +Loot_Generation: + Chance: 20.0 +Mob_Spawning: + Chance: 10.0 +Settings: + Trigger_Chance: '5.0 + %enchantment_level% * 3' + Cost: + Enabled: false + Item: + Material: AIR + Amount: 1 + Ignored_Entity_Types: + - WITHER + - ENDER_DRAGON + Ignored_Spawn_Reasons: + - SPAWNER + - SPAWN_EGG + - DISPENSE_EGG \ No newline at end of file diff --git a/Core/src/main/resources/enchants/thunder.yml b/Core/src/main/resources/enchants/thunder.yml new file mode 100644 index 0000000..d42e8f3 --- /dev/null +++ b/Core/src/main/resources/enchants/thunder.yml @@ -0,0 +1,29 @@ +Is_Treasure: false +Name: Thunder +Tier: rare +Description: + - '%enchantment_trigger_chance%% chance to summon lightning to enemy on hit.' +Level: + Min: 1 + Max: 3 +Anvil: + Merge_Cost: '%enchantment_level%' +Fishing: + Chance: 70.0 +Enchanting_Table: + Level_By_Exp_Cost: '9 * %enchantment_level%' + Chance: 70.0 +Villagers: + Chance: 70.0 +Loot_Generation: + Chance: 70.0 +Mob_Spawning: + Chance: 40.0 +Settings: + Trigger_Chance: '10.0 * %enchantment_level%' + During_Thunderstorm_Only: false + Cost: + Enabled: false + Item: + Material: AIR + Amount: 1 \ No newline at end of file diff --git a/Core/src/main/resources/enchants/treasures.yml b/Core/src/main/resources/enchants/treasures.yml new file mode 100644 index 0000000..2ee454a --- /dev/null +++ b/Core/src/main/resources/enchants/treasures.yml @@ -0,0 +1,65 @@ +Is_Treasure: false +Name: Treasures +Tier: common +Description: + - '%enchantment_trigger_chance%% chance to attempt to find a treasure in mined block.' +Level: + Min: 1 + Max: 5 +Anvil: + Merge_Cost: '%enchantment_level%' +Fishing: + Chance: 70.0 +Enchanting_Table: + Level_By_Exp_Cost: '6 * %enchantment_level%' + Chance: 70.0 +Villagers: + Chance: 75.0 +Loot_Generation: + Chance: 75.0 +Mob_Spawning: + Chance: 0.0 +Settings: + Trigger_Chance: '10.0 + %enchantment_level% * 4.0' + Cost: + Enabled: false + Item: + Material: AIR + Amount: 1 + Particle: + Name: 'REDSTONE' + Data: '200,180,0' + Sound: 'BLOCK_NOTE_BLOCK_BELL' + Treasures: + STONE,GRANITE,ANDESITE,DIORITE: + AIR: 95.0 + BONE_MEAL: 2.0 + CLAY: + AIR: 95.0 + SLIME_BALL: 7.0 + DIRT,PODZOL,COARSE_DIRT,ROOTED_DIRT: + AIR: 95.0 + CLAY_BALL: 0.5 + BOWL: 1.0 + STICK: 2.0 + SAND,RED_SAND: + AIR: 95.0 + GLOWSTONE_DUST: 1.0 + GOLD_NUGGET: 0.3 + SOUL_SAND: + AIR: 95.0 + FLINT: 1.5 + GUNPOWDER: 0.8 + COAL: 0.4 + NETHERRACK: + AIR: 99.0 + BRICK: 0.05 + END_STONE: + AIR: 99.0 + ENDER_PEARL: 0.1 + OAK_LEAVES,BIRCH_LEAVES,SPRUCE_LEAVES,ACACIA_LEAVES,JUNGLE_LEAVES,DARK_OAK_LEAVES: + AIR: 80.0 + APPLE: 15.0 + MOSSY_COBBLESTONE,MOSSY_STONE_BRICKS: + AIR: 50.0 + VINE: 50.0 \ No newline at end of file diff --git a/Core/src/main/resources/enchants/tunnel.yml b/Core/src/main/resources/enchants/tunnel.yml new file mode 100644 index 0000000..d1a0a53 --- /dev/null +++ b/Core/src/main/resources/enchants/tunnel.yml @@ -0,0 +1,29 @@ +Is_Treasure: false +Name: Tunnel +Tier: legendary +Description: + - 'Allows you to mine multiple blocks at once in a shape.' +Level: + Min: 1 + Max: 3 +Anvil: + Merge_Cost: '%enchantment_level%' +Fishing: + Chance: 20.0 +Enchanting_Table: + Level_By_Exp_Cost: '9 * %enchantment_level%' + Chance: 20.0 +Villagers: + Chance: 20.0 +Loot_Generation: + Chance: 30.0 +Mob_Spawning: + Chance: 0.0 +Settings: + Trigger_Chance: '100.0' + Cost: + Enabled: false + Item: + Material: AIR + Amount: 1 + Ignore_When_Sneaking: true diff --git a/Core/src/main/resources/enchants/vampire.yml b/Core/src/main/resources/enchants/vampire.yml new file mode 100644 index 0000000..1d44088 --- /dev/null +++ b/Core/src/main/resources/enchants/vampire.yml @@ -0,0 +1,34 @@ +Is_Treasure: false +Name: Vampire +Tier: exotic +Description: + - '%enchantment_trigger_chance%% chance to heal yourself for %enchantment_heal_amount% heart(s) on hit.' +Level: + Min: 1 + Max: 4 +Anvil: + Merge_Cost: '%enchantment_level%' +Fishing: + Chance: 30.0 +Enchanting_Table: + Level_By_Exp_Cost: '7 * %enchantment_level%' + Chance: 40.0 +Villagers: + Chance: 40.0 +Loot_Generation: + Chance: 40.0 +Mob_Spawning: + Chance: 20.0 +Settings: + Trigger_Chance: '10.0 + %enchantment_level% * 5.0' + Cost: + Enabled: false + Item: + Material: AIR + Amount: 1 + Particle: + Name: 'HEART' + Data: '' + Heal: + Amount: '0.35 * %enchantment_level%' + As_Multiplier: false diff --git a/Core/src/main/resources/enchants/veinminer.yml b/Core/src/main/resources/enchants/veinminer.yml new file mode 100644 index 0000000..11f8161 --- /dev/null +++ b/Core/src/main/resources/enchants/veinminer.yml @@ -0,0 +1,49 @@ +Is_Treasure: false +Name: Veinminer +Tier: rare +Description: + - 'Mines up to %enchantment_block_limit% blocks of the ore vein at once.' +Level: + Min: 1 + Max: 3 +Anvil: + Merge_Cost: '%enchantment_level%' +Fishing: + Chance: 25.0 +Enchanting_Table: + Level_By_Exp_Cost: '10 * %enchantment_level%' + Chance: 25.0 +Villagers: + Chance: 25.0 +Loot_Generation: + Chance: 40.0 +Mob_Spawning: + Chance: 0.0 +Settings: + Trigger_Chance: '100.0' + Cost: + Enabled: false + Item: + Material: AIR + Amount: 1 + Blocks: + Max_At_Once: '6 + %enchantment_level%' + Affected: + - COAL_ORE + - GOLD_ORE + - IRON_ORE + - DIAMOND_ORE + - EMERALD_ORE + - LAPIS_ORE + - REDSTONE_ORE + - COPPER_ORE + - DEEPSLATE_COAL_ORE + - DEEPSLATE_GOLD_ORE + - DEEPSLATE_IRON_ORE + - DEEPSLATE_DIAMOND_ORE + - DEEPSLATE_EMERALD_ORE + - DEEPSLATE_LAPIS_ORE + - DEEPSLATE_REDSTONE_ORE + - DEEPSLATE_COPPER_ORE + - NETHER_QUARTZ_ORE + - NETHER_GOLD_ORE \ No newline at end of file diff --git a/Core/src/main/resources/enchants/venom.yml b/Core/src/main/resources/enchants/venom.yml new file mode 100644 index 0000000..45bc8c6 --- /dev/null +++ b/Core/src/main/resources/enchants/venom.yml @@ -0,0 +1,34 @@ +Is_Treasure: false +Name: Venom +Tier: rare +Description: + - '%enchantment_trigger_chance%% chance to apply %enchantment_potion_type% %enchantment_potion_level% for %enchantment_potion_duration%s. on hit.' +Level: + Min: 1 + Max: 3 +Anvil: + Merge_Cost: '%enchantment_level%' +Fishing: + Chance: 50.0 +Enchanting_Table: + Level_By_Exp_Cost: '9 * %enchantment_level%' + Chance: 50.0 +Villagers: + Chance: 60.0 +Loot_Generation: + Chance: 60.0 +Mob_Spawning: + Chance: 30.0 +Settings: + Trigger_Chance: '30.0 + %enchantment_level% * 10.0' + Cost: + Enabled: false + Item: + Material: AIR + Amount: 1 + Potion_Effect: + Duration: '2.0 + %enchantment_level%' + Level: '%enchantment_level%' + Particle: + Name: SLIME + Data: '' diff --git a/Core/src/main/resources/enchants/village_defender.yml b/Core/src/main/resources/enchants/village_defender.yml new file mode 100644 index 0000000..e735a43 --- /dev/null +++ b/Core/src/main/resources/enchants/village_defender.yml @@ -0,0 +1,34 @@ +Is_Treasure: false +Name: Village Defender +Tier: common +Description: + - '%enchantment_trigger_chance%% chance to inflict %enchantment_damage_amount% more damage on Pillagers.' +Level: + Min: 1 + Max: 5 +Anvil: + Merge_Cost: '%enchantment_level%' +Fishing: + Chance: 60.0 +Enchanting_Table: + Level_By_Exp_Cost: '6 * %enchantment_level%' + Chance: 80.0 +Villagers: + Chance: 80.0 +Loot_Generation: + Chance: 70.0 +Mob_Spawning: + Chance: 0.0 +Settings: + Trigger_Chance: '100.0' + Cost: + Enabled: false + Item: + Material: AIR + Amount: 1 + Particle: + Name: 'VILLAGER_ANGRY' + Data: '' + Damage: + As_Modifier: false + Formula: '0.5 * %enchantment_level%' diff --git a/Core/src/main/resources/enchants/wither.yml b/Core/src/main/resources/enchants/wither.yml new file mode 100644 index 0000000..c866f72 --- /dev/null +++ b/Core/src/main/resources/enchants/wither.yml @@ -0,0 +1,34 @@ +Is_Treasure: false +Name: Wither +Tier: rare +Description: + - '%enchantment_trigger_chance%% chance to apply %enchantment_potion_type% %enchantment_potion_level% for %enchantment_potion_duration%s. on hit.' +Level: + Min: 1 + Max: 3 +Anvil: + Merge_Cost: '%enchantment_level%' +Fishing: + Chance: 20.0 +Enchanting_Table: + Level_By_Exp_Cost: '9 * %enchantment_level%' + Chance: 20.0 +Villagers: + Chance: 30.0 +Loot_Generation: + Chance: 30.0 +Mob_Spawning: + Chance: 15.0 +Settings: + Trigger_Chance: '10.0 + %enchantment_level% * 5' + Cost: + Enabled: false + Item: + Material: AIR + Amount: 1 + Potion_Effect: + Duration: '3.0 + %enchantment_level%' + Level: '%enchantment_level%' + Particle: + Name: '' + Data: '' diff --git a/Core/src/main/resources/enchants/withered_arrows.yml b/Core/src/main/resources/enchants/withered_arrows.yml new file mode 100644 index 0000000..8002bd9 --- /dev/null +++ b/Core/src/main/resources/enchants/withered_arrows.yml @@ -0,0 +1,31 @@ +Is_Treasure: false +Name: Withered Arrows +Tier: rare +Description: + - '%enchantment_trigger_chance%% chance to launch an arrow with %enchantment_potion_type% %enchantment_potion_level% (%enchantment_potion_duration%s.)' +Level: + Min: 1 + Max: 3 +Anvil: + Merge_Cost: '%enchantment_level%' +Fishing: + Chance: 25.0 +Enchanting_Table: + Level_By_Exp_Cost: '9 * %enchantment_level%' + Chance: 25.0 +Villagers: + Chance: 25.0 +Loot_Generation: + Chance: 25.0 +Mob_Spawning: + Chance: 15.0 +Settings: + Trigger_Chance: '15.0 + %enchantment_level% * 5.0' + Cost: + Enabled: false + Item: + Material: AIR + Amount: 1 + Potion_Effect: + Duration: '2.5 + %enchantment_level% * 0.75' + Level: '%enchantment_level%' \ No newline at end of file diff --git a/Core/src/main/resources/gui.enchants.yml b/Core/src/main/resources/gui.enchants.yml new file mode 100644 index 0000000..b47551e --- /dev/null +++ b/Core/src/main/resources/gui.enchants.yml @@ -0,0 +1,60 @@ +# Menu title. +Title: 'Excellent Enchants List' +# Menu size. Allowed values: 9, 18, 27, 36, 45, 54. +Size: 36 + +# Enchantment display settings. +Enchantments: + # Enchantment display item options. + Icon: + Material: 'ENCHANTED_BOOK' + Name: '%enchantment_name_formatted%' + Lore: + - '%enchantment_description%' + - '&7' + - '&a▸ &7Tier: &a%enchantment_tier%' + - '&a▸ &7Applies to: &a%enchantment_fit_item_types%' + - '&a▸ &7Levels: &a%enchantment_level_min% &7- &a%enchantment_level_max%' + - '&7' + - '&c&lConflicts with:' + - '&c▸ &4%enchantment_conflicts%' + - '&7' + - '&e&lItem Cost to Use:' + - '&e▸ &7%enchantment_cost_item%' + - '&7' + - '&b&lObtain Chance:' + - '&b▸ &7Enchanting Table: &b%enchantment_obtain_chance_enchanting%%' + - '&b▸ &7Villager Trade: &b%enchantment_obtain_chance_villager%%' + - '&b▸ &7Loot Generation: &b%enchantment_obtain_chance_loot_generation%%' + - '&b▸ &7Fishing: &b%enchantment_obtain_chance_fishing%%' + - '&b▸ &7Mob Spawning: &b%enchantment_obtain_chance_mob_spawning%%' + - '&7' + - '&d&lClick to switch enchantment levels!' + # Menu slots to display enchantments. + Slots: 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26 + +# List of custom menu items. +Content: + page_next: + Display: + default: + Priority: 0 + Item: + Material: ARROW + Name: '&aNext Page' + Lore: + - '&7Click to move &aForward&7!' + Slots: 35 + Type: PAGE_NEXT + + page_prev: + Display: + default: + Priority: 0 + Item: + Material: ARROW + Name: '&cPrevious Page' + Lore: + - '&7Click to move &cBack&7!' + Slots: 27 + Type: PAGE_PREVIOUS \ No newline at end of file diff --git a/Core/src/main/resources/lang/messages_cn.yml b/Core/src/main/resources/lang/messages_cn.yml new file mode 100644 index 0000000..ae013c2 --- /dev/null +++ b/Core/src/main/resources/lang/messages_cn.yml @@ -0,0 +1,18 @@ +# Tranlsated by @qsefthuopq +# Last updated:2021-03-15 +Command: + Enchant: + Usage: <附魔名> <等级> + Desc: 附魔你手中的物品。 + Done: '&a附魔成功!' + Book: + Usage: <玩家> <附魔名> <等级> + Desc: 给予自定义附魔书。 + Done: 已给予&6%player%&6%enchant%&7附魔书。 + TierBook: + Usage: <玩家> <品质> <等级> + Desc: 给予自定义品质的附魔书 + Error: '&c无效品质!' + Done: 已给予&6%player&6%enchant%&7附魔书。 +Error: + NoEnchant: '&c没有这种附魔' \ No newline at end of file diff --git a/Core/src/main/resources/lang/messages_en.yml b/Core/src/main/resources/lang/messages_en.yml new file mode 100644 index 0000000..e69de29 diff --git a/Core/src/main/resources/lang/messages_pl.yml b/Core/src/main/resources/lang/messages_pl.yml new file mode 100644 index 0000000..0470bfb --- /dev/null +++ b/Core/src/main/resources/lang/messages_pl.yml @@ -0,0 +1,18 @@ +# Przetłumaczone przez nitolar play +# Tłumaczenie utworzono: 06.11.2020 +Command: + Enchant: + Usage: + Desc: Zaklnij przedmiot w swojej ręce. + Done: '&aSukcesywnie Zaklnięto!' + Book: + Usage: + Desc: Dodaje niestandardowo zaklniętą książke. + Done: Dodano zaklniętą ksiązke z zaklęciem &6%enchant%&7 do gracza &6%player%&7. + TierBook: + Usage: <żadkość> + 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. +Error: + NoEnchant: '&cNie ma takiego zaklęcia.' \ No newline at end of file diff --git a/Core/src/main/resources/lang/messages_ru.yml b/Core/src/main/resources/lang/messages_ru.yml new file mode 100644 index 0000000..9eff883 --- /dev/null +++ b/Core/src/main/resources/lang/messages_ru.yml @@ -0,0 +1,35 @@ +Command: + List: + Desc: Список всех пользовательских зачарований. + Enchant: + Usage: <зачарование> <уровень> + Desc: Зачаровывает предмет в вашей руке. + Done: '&aУспешно зачарован!' + Book: + Usage: <игрок> <зачарование> <уровень> + Desc: Даёт кастомную книгу зачарования. + Done: Выдана книга зачарованя &6%enchant%&7 &6%player%&7. + TierBook: + Usage: <игрок> <клас> <уровень> + Desc: Даёт зачарованную книгу. + Error: '&cНеверный уровень!' + Done: Выдан зачарованная книга класа &6%tier%&7 &6%player%&7. +Error: + NoEnchant: '&cНет такого зачарования.' +EnchantmentTarget: + ALL: All + ARMOR: Armor + ARMOR_FEET: Armor Feet + ARMOR_LEGS: Armor Legs + ARMOR_TORSO: Armor Torso + ARMOR_HEAD: Armor Head + WEAPON: Weapon + TOOL: Tool + BOW: Bow + FISHING_ROD: Fishing Rod + BREAKABLE: Breakable + WEARABLE: Wearable + TRIDENT: Trident + CROSSBOW: Crossbow + VANISHABLE: Vanishable + BOW_AND_CROSSBOW: Bow And Crossbow \ No newline at end of file diff --git a/Core/src/main/resources/plugin.yml b/Core/src/main/resources/plugin.yml new file mode 100644 index 0000000..386117e --- /dev/null +++ b/Core/src/main/resources/plugin.yml @@ -0,0 +1,44 @@ +main: su.nightexpress.excellentenchants.ExcellentEnchants +version: '${project.version}' +name: ExcellentEnchants +author: NightExpress +desciption: Vanilla-like enchants for your server. +depend: [ NexEngine ] +softdepend: [ Towny, Residence, WorldGuard, GriefPrevention, Lands ] +api-version: 1.16 +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 + diff --git a/NMS/pom.xml b/NMS/pom.xml new file mode 100644 index 0000000..4cd50d2 --- /dev/null +++ b/NMS/pom.xml @@ -0,0 +1,34 @@ + + + + ExcellentEnchants + su.nightexpress.excellentenchants + 3.1.0 + + 4.0.0 + + NMS + + + 16 + 16 + + + + + md_5-releases + https://repo.md-5.net/content/repositories/releases/ + + + + + + org.spigotmc + spigot-api + 1.18.1-R0.1-SNAPSHOT + + + + \ No newline at end of file diff --git a/NMS/src/main/java/su/nightexpress/excellentenchants/nms/EnchantNMS.java b/NMS/src/main/java/su/nightexpress/excellentenchants/nms/EnchantNMS.java new file mode 100644 index 0000000..c675ddc --- /dev/null +++ b/NMS/src/main/java/su/nightexpress/excellentenchants/nms/EnchantNMS.java @@ -0,0 +1,13 @@ +package su.nightexpress.excellentenchants.nms; + +import org.bukkit.Location; +import org.bukkit.block.Block; +import org.bukkit.entity.LivingEntity; +import org.jetbrains.annotations.NotNull; + +import java.util.Set; + +public interface EnchantNMS { + + @NotNull Set handleFlameWalker(@NotNull LivingEntity entity, @NotNull Location location, int level); +} diff --git a/V1_16_R1/pom.xml b/V1_16_R1/pom.xml new file mode 100644 index 0000000..fd5b5d5 --- /dev/null +++ b/V1_16_R1/pom.xml @@ -0,0 +1,32 @@ + + + + ExcellentEnchants + su.nightexpress.excellentenchants + 3.1.0 + + 4.0.0 + + V1_16_R1 + + + 16 + 16 + + + + + org.spigotmc + spigot + 1.16.5-R0.1-SNAPSHOT + + + su.nightexpress.excellentenchants + NMS + 3.1.0 + + + + \ No newline at end of file diff --git a/V1_16_R1/src/main/java/su/nightexpress/excellentenchants/nms/V1_16_R3.java b/V1_16_R1/src/main/java/su/nightexpress/excellentenchants/nms/V1_16_R3.java new file mode 100644 index 0000000..3eddc29 --- /dev/null +++ b/V1_16_R1/src/main/java/su/nightexpress/excellentenchants/nms/V1_16_R3.java @@ -0,0 +1,51 @@ +package su.nightexpress.excellentenchants.nms; + +import net.minecraft.server.v1_16_R3.*; +import org.bukkit.Location; +import org.bukkit.block.Block; +import org.bukkit.craftbukkit.v1_16_R3.CraftWorld; +import org.bukkit.craftbukkit.v1_16_R3.entity.CraftLivingEntity; +import org.bukkit.craftbukkit.v1_16_R3.event.CraftEventFactory; +import org.bukkit.entity.LivingEntity; +import org.jetbrains.annotations.NotNull; + +import java.util.HashSet; +import java.util.Set; + +public class V1_16_R3 implements EnchantNMS { + + @Override + @NotNull + public Set handleFlameWalker(@NotNull LivingEntity entity1, @NotNull Location location, int level) { + EntityLiving entity = ((CraftLivingEntity) entity1).getHandle(); + BlockPosition pos = new BlockPosition(location.getX(), location.getY(), location.getZ()); + World world = ((CraftWorld) entity1.getWorld()).getHandle(); + + IBlockData bStone = Blocks.MAGMA_BLOCK.getBlockData(); + float rad = Math.min(16, 2 + level); + + BlockPosition.MutableBlockPosition posMut = new BlockPosition.MutableBlockPosition(); + Set blocks = new HashSet<>(); + for (BlockPosition bNear : BlockPosition.a(pos.a(-rad, -1.0, -rad), pos.a(rad, -1.0, rad))) { + + if (!bNear.a(entity.getPositionVector(), rad)) continue; + posMut.d(bNear.getX(), bNear.getY() + 1, bNear.getZ()); + + IBlockData bLavaUp = world.getType(posMut); + IBlockData bLava = world.getType(bNear); + + if (!bLavaUp.isAir()) continue; + if (bLava.getMaterial() != Material.LAVA) continue; + if (bLava.get(BlockFluids.LEVEL) != 0) continue; + if (!bStone.canPlace(world, bNear)) continue; + if (!world.a(bStone, bNear, VoxelShapeCollision.a())) continue; + if (!CraftEventFactory.handleBlockFormEvent(world, bNear, bStone, entity)) continue; + + world.getBlockTickList().a(bNear, Blocks.MAGMA_BLOCK, MathHelper.nextInt(entity.getRandom(), 60, 120)); + + Location loc2 = new Location(world.getWorld(), bNear.getX(), bNear.getY(), bNear.getZ()); + blocks.add(loc2.getBlock()); + } + return blocks; + } +} diff --git a/V1_17_R1/pom.xml b/V1_17_R1/pom.xml new file mode 100644 index 0000000..66388b0 --- /dev/null +++ b/V1_17_R1/pom.xml @@ -0,0 +1,32 @@ + + + + ExcellentEnchants + su.nightexpress.excellentenchants + 3.1.0 + + 4.0.0 + + V1_17_R1 + + + 16 + 16 + + + + + org.spigotmc + spigot + 1.17.1-R0.1-SNAPSHOT + + + su.nightexpress.excellentenchants + NMS + 3.1.0 + + + + \ No newline at end of file diff --git a/V1_17_R1/src/main/java/su/nightexpress/excellentenchants/nms/V1_17_R1.java b/V1_17_R1/src/main/java/su/nightexpress/excellentenchants/nms/V1_17_R1.java new file mode 100644 index 0000000..0476c3b --- /dev/null +++ b/V1_17_R1/src/main/java/su/nightexpress/excellentenchants/nms/V1_17_R1.java @@ -0,0 +1,62 @@ +package su.nightexpress.excellentenchants.nms; + +import net.minecraft.core.BlockPosition; +import net.minecraft.util.MathHelper; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.World; +import net.minecraft.world.level.block.BlockFluids; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.IBlockData; +import net.minecraft.world.phys.shapes.VoxelShapeCollision; +import org.bukkit.Location; +import org.bukkit.block.Block; +import org.bukkit.craftbukkit.v1_17_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_17_R1.entity.CraftLivingEntity; +import org.bukkit.craftbukkit.v1_17_R1.event.CraftEventFactory; +import org.bukkit.entity.LivingEntity; +import org.jetbrains.annotations.NotNull; +import su.nexmedia.engine.utils.random.Rnd; + +import java.util.HashSet; +import java.util.Set; + +public class V1_17_R1 implements EnchantNMS { + + @Override + @NotNull + public Set handleFlameWalker(@NotNull LivingEntity entity1, @NotNull Location location, int level) { + Entity entity = ((CraftLivingEntity) entity1).getHandle(); + BlockPosition pos = new BlockPosition(location.getX(), location.getY(), location.getZ()); + World world = ((CraftWorld) entity1.getWorld()).getHandle(); + + IBlockData bStone = Blocks.iX.getBlockData(); + float rad = Math.min(16, 2 + level); + + org.bukkit.World w1 = entity1.getWorld(); + + BlockPosition.MutableBlockPosition posMut = new BlockPosition.MutableBlockPosition(); + Set blocks = new HashSet<>(); + for (BlockPosition bNear : BlockPosition.a(pos.b(-rad, -1.0, -rad), pos.b(rad, -1.0, rad))) { + if (!bNear.a(entity.getPositionVector(), rad)) continue; + posMut.d(bNear.getX(), bNear.getY() + 1, bNear.getZ()); + + IBlockData bLavaUp = world.getType(posMut); + IBlockData bLava = world.getType(bNear); + + if (!bLavaUp.isAir()) continue; + // меня заебало нахуй искать и подбирать ебучую лаву в NMS + Block normal = w1.getBlockAt(bNear.getX(), bNear.getY(), bNear.getZ()); + if (normal.getType() != org.bukkit.Material.LAVA) continue; + if (bLava.get(BlockFluids.a) != 0) continue; + if (!bStone.canPlace(world, bNear)) continue; + if (!world.a(bStone, bNear, VoxelShapeCollision.a())) continue; + if (!CraftEventFactory.handleBlockFormEvent(world, bNear, bStone, entity)) continue; + + world.getBlockTickList().a(bNear, Blocks.iX, MathHelper.nextInt(Rnd.rnd, 60, 120)); + + Location loc2 = new Location(world.getWorld(), bNear.getX(), bNear.getY(), bNear.getZ()); + blocks.add(loc2.getBlock()); + } + return blocks; + } +} diff --git a/V1_18_R1/pom.xml b/V1_18_R1/pom.xml new file mode 100644 index 0000000..d1e71a9 --- /dev/null +++ b/V1_18_R1/pom.xml @@ -0,0 +1,32 @@ + + + + ExcellentEnchants + su.nightexpress.excellentenchants + 3.1.0 + + 4.0.0 + + V1_18_R1 + + + 16 + 16 + + + + + org.spigotmc + spigot + 1.18.1-R0.1-SNAPSHOT + + + su.nightexpress.excellentenchants + NMS + 3.1.0 + + + + \ No newline at end of file diff --git a/V1_18_R1/src/main/java/su/nightexpress/excellentenchants/nms/V1_18_R1.java b/V1_18_R1/src/main/java/su/nightexpress/excellentenchants/nms/V1_18_R1.java new file mode 100644 index 0000000..fdfe456 --- /dev/null +++ b/V1_18_R1/src/main/java/su/nightexpress/excellentenchants/nms/V1_18_R1.java @@ -0,0 +1,61 @@ +package su.nightexpress.excellentenchants.nms; + +import net.minecraft.core.BlockPosition; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.item.ItemBow; +import net.minecraft.world.level.IWorldReader; +import net.minecraft.world.level.World; +import net.minecraft.world.level.block.BlockFluids; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.IBlockData; +import net.minecraft.world.phys.shapes.VoxelShapeCollision; +import org.bukkit.Location; +import org.bukkit.block.Block; +import org.bukkit.craftbukkit.v1_18_R1.CraftWorld; +import org.bukkit.craftbukkit.v1_18_R1.entity.CraftLivingEntity; +import org.bukkit.craftbukkit.v1_18_R1.event.CraftEventFactory; +import org.bukkit.entity.LivingEntity; +import org.jetbrains.annotations.NotNull; + +import java.util.HashSet; +import java.util.Set; + +public class V1_18_R1 implements EnchantNMS { + + @Override + @NotNull + public Set handleFlameWalker(@NotNull LivingEntity entity1, @NotNull Location location, int level) { + Entity entity = ((CraftLivingEntity) entity1).getHandle(); + BlockPosition pos = new BlockPosition(location.getX(), location.getY(), location.getZ()); + World world = ((CraftWorld) entity1.getWorld()).getHandle(); + + IBlockData bStone = Blocks.iX.n(); + float rad = Math.min(16, 2 + level); + + org.bukkit.World w1 = entity1.getWorld(); + BlockPosition.MutableBlockPosition posMut = new BlockPosition.MutableBlockPosition(); + + Set blocks = new HashSet<>(); + for (BlockPosition bNear : BlockPosition.a(pos.a(-rad, -1.0, -rad), pos.a(rad, -1.0, rad))) { + if (!bNear.a(entity.cV(), rad)) continue; + posMut.d(bNear.u(), bNear.v() + 1, bNear.w()); + + IBlockData bLavaUp = world.a_(posMut); + IBlockData bLava = world.a_(bNear); + + if (!bLavaUp.g()) continue; + // меня заебало нахуй искать и подбирать ебучую лаву в NMS + Block normal = w1.getBlockAt(bNear.u(), bNear.v(), bNear.w()); + if (normal.getType() != org.bukkit.Material.LAVA) continue; + if (bLava.c(BlockFluids.a) != 0) continue; + if (!bStone.a((IWorldReader) world, bNear)) continue; + if (!world.a(bStone, bNear, VoxelShapeCollision.a())) continue; + if (!CraftEventFactory.handleBlockFormEvent(world, bNear, bStone, entity)) continue; + world.N().a(bNear, Blocks.iX); + + Location loc2 = new Location(world.getWorld(), bNear.u(), bNear.v(), bNear.w()); + blocks.add(loc2.getBlock()); + } + return blocks; + } +} diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..776567d --- /dev/null +++ b/pom.xml @@ -0,0 +1,37 @@ + + + 4.0.0 + + su.nightexpress.excellentenchants + ExcellentEnchants + pom + 3.1.0 + + Core + NMS + V1_17_R1 + V1_16_R1 + V1_18_R1 + + + + 16 + 16 + + + + + su.nexmedia + NexEngine + 2.1.0 + + + su.nexmedia + NexEngine_CustomEffects + 1.0.0 + + + + \ No newline at end of file