This commit is contained in:
nulli0n 2022-01-21 19:24:32 +05:00
commit ab5b19db4d
160 changed files with 9581 additions and 0 deletions

13
.gitignore vendored Normal file
View File

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

92
Core/pom.xml Normal file
View File

@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>ExcellentEnchants</artifactId>
<groupId>su.nightexpress.excellentenchants</groupId>
<version>3.1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>Core</artifactId>
<properties>
<maven.compiler.source>16</maven.compiler.source>
<maven.compiler.target>16</maven.compiler.target>
</properties>
<repositories>
<repository>
<id>md_5-releases</id>
<url>https://repo.md-5.net/content/repositories/releases/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.18.1-R0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>su.nightexpress.excellentenchants</groupId>
<artifactId>NMS</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>su.nightexpress.excellentenchants</groupId>
<artifactId>V1_16_R1</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>su.nightexpress.excellentenchants</groupId>
<artifactId>V1_17_R1</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>su.nightexpress.excellentenchants</groupId>
<artifactId>V1_18_R1</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>fr.neatmonster</groupId>
<artifactId>nocheatplus</artifactId>
<version>3.16.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory> <!-- (your resources folder location) -->
<filtering>true</filtering> <!-- this is the important part, it's what replaces, filters, all placeholders in the resources folder (such as ${project.version} in plugin.yml) -->
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<outputFile>${project.parent.basedir}\target\${project.parent.name}-${project.version}.jar</outputFile>
<artifactSet>
<includes>
<include>su.nightexpress.excellentenchants*</include>
</includes>
</artifactSet>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

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

View File

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

View File

@ -0,0 +1,6 @@
package su.nightexpress.excellentenchants.api.enchantment;
public enum EnchantPriority {
LOWEST, LOW, MEDIUM, HIGH, HIGHEST
}

View File

@ -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<String> description;
private final Set<Enchantment> conflicts;
protected boolean isTreasure;
protected int levelMin;
protected int levelMax;
protected Scaler levelByEnchantCost;
protected Scaler anvilMergeCost;
protected Map<ObtainType, Double> 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<String> 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<String> 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<String> getDescription() {
return this.description;
}
@NotNull
public List<String> getDescription(int level) {
List<String> description = new ArrayList<>(this.description);
description.replaceAll(this.replacePlaceholders(level));
return description;
}
@NotNull
public Set<Enchantment> 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<Map.Entry<Integer, Double>> 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;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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<ItemStack> getCustomDrops(@NotNull Player player, @NotNull ItemStack item, @NotNull Block block, int level);
boolean isEventMustHaveDrops();
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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<String> ENCHANTMENTS_DISABLED;
public static Map<String, Set<String>> 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<ObtainType, ObtainSettings> OBTAIN_SETTINGS;
private static Map<String, EnchantTier> 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<ObtainType, Double> 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<String> 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<EnchantTier> getTiers() {
return TIERS.values();
}
@NotNull
public static List<String> getTierIds() {
return new ArrayList<>(TIERS.keySet());
}
@Nullable
public static EnchantTier getTierByChance(@NotNull ObtainType obtainType) {
Map<EnchantTier, Double> 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<String> formatDescription(@NotNull List<String> description) {
return new ArrayList<>(description.stream().map(line -> ENCHANTMENTS_DESCRIPTION_FORMAT.replace("%description%", line)).toList());
}
}

View File

@ -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, "<enchant> <level>");
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, "<player> <enchant> <level>");
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, "<player> <tier> <level>");
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.");
}

View File

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

View File

@ -0,0 +1,6 @@
package su.nightexpress.excellentenchants.hook;
public class HookId {
public static final String NCP = "NoCheatPlus";
}

View File

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

View File

@ -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<ExcellentEnchants> {
private EnchantListGUI enchantListGUI;
private ArrowTrailsTask arrowTrailsTask;
private EnchantEffectPassiveTask enchantEffectPassiveTask;
private static final Map<UUID, ItemStack> 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<ExcellentEnchant, Integer> 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<ExcellentEnchant> list = new ArrayList<>(excellents.keySet());
Collections.reverse(list);
list.forEach(excellent -> {
List<String> 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<ExcellentEnchant, Integer> 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 <T> Map<T, Integer> getItemCustomEnchants(@NotNull ItemStack item, @NotNull Class<T> 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<Enchantment, Integer> 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<EnchantTier> getTiers() {
return Config.getTiers();
}
@NotNull
public static List<String> 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());
}
}

View File

@ -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<ExcellentEnchant> 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<NamespacedKey, Enchantment> byKey = (Map<NamespacedKey, Enchantment>) Reflex.getFieldValue(Enchantment.class, "byKey");
Map<String, Enchantment> byName = (Map<String, Enchantment>) 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 extends ExcellentEnchant> T init(@NotNull Class<T> 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));
}
}

View File

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

View File

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

View File

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

View File

@ -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<Block, Long> 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<ExcellentEnchants> {
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;
});
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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<Integer, NamespacedKey> 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<ItemStack> 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()));
}
}

View File

@ -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<Material, Material> 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<ItemStack> getCustomDrops(@NotNull Player player, @NotNull ItemStack item, @NotNull Block block, int level) {
if (block.getState() instanceof Container) return Collections.emptyList();
List<ItemStack> 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<ItemStack> smelt(@NotNull List<ItemStack> 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<ItemStack> defaults = plugin.getNMS().getBlockDrops(block, player, item);
List<ItemStack> 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;
}
}

View File

@ -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<String> 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<ItemStack> 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<CustomDropEnchant, Integer> 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;
}
}

View File

@ -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<Material, Map<Material, Double>> treasures;
private final Predicate<Block> 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<Material, Double> 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<ItemStack> 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<Material, Double> 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<ItemStack> 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;
}
}

View File

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

View File

@ -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<Material> 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<Material> getBlocksAffected() {
return this.blocksAffected;
}
public int getBlocksLimit(int level) {
return (int) this.blocksLimit.getValue(level);
}
@Override
@NotNull
public UnaryOperator<String> 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<Block> 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<Block> ores = new HashSet<>();
Set<Block> 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<Block> 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;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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<String> ignoredEntityTypes;
private final String headName;
private final Map<String, String> 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;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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<EntityType, Map<Material, Map.Entry<int[], Double>>> 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<Material, Map.Entry<int[], Double>> 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<int[], Double> 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<Material, Map.Entry<int[], Double>> 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;
}
}

View File

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

View File

@ -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<String> ignoredEntityTypes;
private final Set<String> 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);
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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<ExcellentEnchants> {
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<ExcellentEnchant, Integer> 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<ExcellentEnchant, Integer> 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<ExcellentEnchant, Integer> 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<ExcellentEnchant, Integer> 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);
}
}

View File

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

View File

@ -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<ExcellentEnchants> {
private final ItemStack enchantIcon;
private final int[] enchantSlots;
private final NamespacedKey keyLevel;
private final Map<String, Map<Integer, ItemStack>> 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<String> 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<ExcellentEnchant> list = new ArrayList<>(EnchantRegister.ENCHANT_LIST.stream().
sorted(Comparator.comparing(ExcellentEnchant::getName)).toList());
List<List<ExcellentEnchant>> 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;
}
}

View File

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

View File

@ -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<ObtainType, Double> chance;
private final Set<ExcellentEnchant> enchants;
public EnchantTier(@NotNull String id, int priority, @NotNull String name, @NotNull String color, @NotNull Map<ObtainType, Double> 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<ObtainType, Double> getChance() {
return this.chance;
}
public double getChance(@NotNull ObtainType obtainType) {
return this.getChance().getOrDefault(obtainType, 0D);
}
@NotNull
public Set<ExcellentEnchant> getEnchants() {
return this.enchants;
}
@NotNull
public Set<ExcellentEnchant> getEnchants(@NotNull ObtainType obtainType) {
return this.getEnchants(obtainType, null);
}
@NotNull
public Set<ExcellentEnchant> getEnchants(@NotNull ObtainType obtainType, @Nullable ItemStack item) {
Set<ExcellentEnchant> 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<ExcellentEnchant, Double> 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);
}
}

View File

@ -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<ExcellentEnchants> {
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<ItemStack> 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<LivingEntity> 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;
}
}

View File

@ -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<ExcellentEnchants> {
private static final Map<Projectile, Set<Map.Entry<String, String>>> 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));
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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%'

View File

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

View File

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

View File

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

View File

@ -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%'

View File

@ -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%'

Some files were not shown because too many files have changed in this diff Show More