This commit is contained in:
BuildTools 2024-02-05 04:26:03 +05:00
commit 87358cddb4
163 changed files with 15729 additions and 0 deletions

19
.gitignore vendored Normal file
View File

@ -0,0 +1,19 @@
/.idea/
/target/
/pom.xml.versionsBackup
/Core/target/
/Core/pom.xml.versionsBackup
/NMS/target/
/NMS/pom.xml.versionsBackup
/V1_18_R2/target/
/V1_18_R2/pom.xml.versionsBackup
/V1_19_R3/target/
/V1_19_R3/pom.xml.versionsBackup
/V1_20_R1/target/
/V1_20_R1/pom.xml.versionsBackup
/V1_20_R2/target/
/V1_20_R2/pom.xml.versionsBackup
/V1_20_R3/target/
/V1_20_R3/pom.xml.versionsBackup
/API/target/
/API/pom.xml.versionsBackup

19
API/pom.xml Normal file
View File

@ -0,0 +1,19 @@
<?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.6.5</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>API</artifactId>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
</project>

View File

@ -0,0 +1,94 @@
package su.nightexpress.excellentenchants.api.enchantment;
import org.bukkit.Keyed;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.LivingEntity;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import su.nexmedia.engine.api.config.JYML;
import java.util.List;
import java.util.Set;
public interface IEnchantment extends Keyed {
boolean isAvailableToUse(@NotNull LivingEntity entity);
@NotNull JYML getConfig();
@NotNull String getId();
@NotNull String getDisplayName();
@NotNull String getNameFormatted(int level);
@NotNull String getNameFormatted(int level, int charges);
@NotNull List<String> getDescription();
@NotNull List<String> getDescription(int level);
@NotNull Set<String> getConflicts();
@NotNull ITier getTier();
@NotNull EnchantmentTarget getCategory();
ItemCategory[] getFitItemTypes();
int getMaxLevel();
int getStartLevel();
int getLevelByEnchantCost(int expLevel);
double getObtainChance(@NotNull ObtainType obtainType);
int getObtainLevelMin(@NotNull ObtainType obtainType);
int getObtainLevelMax(@NotNull ObtainType obtainType);
int generateLevel(@NotNull ObtainType obtainType);
int getAnvilMergeCost(int level);
//@Deprecated
//boolean conflictsWith(@NotNull Enchantment enchantment);
boolean checkEnchantCategory(@NotNull ItemStack item);
boolean checkItemCategory(@NotNull ItemStack item);
boolean isCurse();
boolean isTreasure();
boolean isTradeable();
boolean isDiscoverable();
boolean isChargesEnabled();
int getChargesMax(int level);
int getChargesConsumeAmount(int level);
int getChargesRechargeAmount(int level);
@NotNull ItemStack getChargesFuel();
boolean isChargesFuel(@NotNull ItemStack item);
int getCharges(@NotNull ItemStack item);
boolean isFullOfCharges(@NotNull ItemStack item);
boolean isOutOfCharges(@NotNull ItemStack item);
void consumeCharges(@NotNull ItemStack item, int level);
void consumeChargesNoUpdate(@NotNull ItemStack item, int level);
EquipmentSlot[] getSlots();
}

View File

@ -0,0 +1,21 @@
package su.nightexpress.excellentenchants.api.enchantment;
import org.jetbrains.annotations.NotNull;
import su.nexmedia.engine.api.placeholder.Placeholder;
import java.util.Map;
public interface ITier extends Placeholder {
@NotNull String getId();
int getPriority();
@NotNull String getName();
@NotNull String getColor();
@NotNull Map<ObtainType, Double> getChance();
double getChance(@NotNull ObtainType obtainType);
}

View File

@ -0,0 +1,128 @@
package su.nightexpress.excellentenchants.api.enchantment;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import su.nexmedia.engine.utils.ItemUtil;
import java.util.function.Predicate;
public enum ItemCategory {
HELMET(ItemUtil::isHelmet),
CHESTPLATE(ItemUtil::isChestplate),
LEGGINGS(ItemUtil::isLeggings),
BOOTS(ItemUtil::isBoots),
ELYTRA(item -> item.getType() == Material.ELYTRA),
SWORD(ItemUtil::isSword),
TRIDENT(ItemUtil::isTrident),
AXE(ItemUtil::isAxe),
BOW(item -> item.getType() == Material.BOW),
CROSSBOW(item -> item.getType() == Material.CROSSBOW),
HOE(ItemUtil::isHoe),
PICKAXE(ItemUtil::isPickaxe),
SHOVEL(ItemUtil::isShovel),
FISHING_ROD(ItemUtil::isFishingRod),
//@Deprecated WEAPON(item -> SWORD.isIncluded(item) || TRIDENT.isIncluded(item)),
TOOL(ItemUtil::isTool),
//@Deprecated ARMOR(ItemUtil::isArmor),
//UNIVERSAL(item -> WEAPON.isIncluded(item) || TOOL.isIncluded(item) || ARMOR.isIncluded(item)),
;
private Predicate<ItemStack> predicate;
ItemCategory(@NotNull Predicate<ItemStack> predicate) {
this.setPredicate(predicate);
}
@NotNull
public Predicate<ItemStack> getPredicate() {
return predicate;
}
public void setPredicate(@NotNull Predicate<ItemStack> predicate) {
this.predicate = predicate;
}
@Deprecated
public void patchPredicate(@NotNull Predicate<ItemStack> extra) {
//this.setPredicate(item -> this.getPredicate().test(item) || (extra.test(item)));
}
/*public EquipmentSlot[] getSlots() {
return switch (this) {
case BOW, CROSSBOW, TRIDENT, FISHING_ROD, WEAPON, TOOL, HOE, PICKAXE, AXE, SWORD, SHOVEL ->
new EquipmentSlot[]{EquipmentSlot.HAND};
case HELMET -> new EquipmentSlot[]{EquipmentSlot.HEAD};
case CHESTPLATE, ELYTRA -> new EquipmentSlot[]{EquipmentSlot.CHEST};
case LEGGINGS -> new EquipmentSlot[]{EquipmentSlot.LEGS};
case BOOTS -> new EquipmentSlot[]{EquipmentSlot.FEET};
case ARMOR -> new EquipmentSlot[]{EquipmentSlot.HEAD, EquipmentSlot.CHEST, EquipmentSlot.LEGS, EquipmentSlot.FEET};
case UNIVERSAL -> new EquipmentSlot[]{EquipmentSlot.HAND, EquipmentSlot.HEAD, EquipmentSlot.CHEST, EquipmentSlot.LEGS, EquipmentSlot.FEET};
};
}
@NotNull
public EnchantmentTarget getEnchantmentTarget() {
return switch (this) {
case ARMOR -> EnchantmentTarget.ARMOR;
case BOOTS -> EnchantmentTarget.ARMOR_FEET;
case LEGGINGS -> EnchantmentTarget.ARMOR_LEGS;
case CHESTPLATE, ELYTRA -> EnchantmentTarget.ARMOR_TORSO;
case HELMET -> EnchantmentTarget.ARMOR_HEAD;
case WEAPON, SWORD -> EnchantmentTarget.WEAPON;
case TOOL, AXE, HOE, SHOVEL, PICKAXE -> EnchantmentTarget.TOOL;
case BOW -> EnchantmentTarget.BOW;
case FISHING_ROD -> EnchantmentTarget.FISHING_ROD;
case TRIDENT -> EnchantmentTarget.TRIDENT;
case CROSSBOW -> EnchantmentTarget.CROSSBOW;
case UNIVERSAL -> EnchantmentTarget.WEARABLE;
};
}
@NotNull
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;
case BREAKABLE, WEARABLE -> UNIVERSAL;
default -> throw new IllegalStateException("Unexpected value: " + target);
};
}*/
public boolean isIncluded(@NotNull ItemStack item) {
return this.getPredicate().test(item);
/*return switch (this) {
case UNIVERSAL -> ARMOR.isIncluded(item) || WEAPON.isIncluded(item) || TOOL.isIncluded(item) || BOW.isIncluded(item) || FISHING_ROD.isIncluded(item) || ELYTRA.isIncluded(item);
case HELMET -> ItemUtil.isHelmet(item);
case CHESTPLATE -> ItemUtil.isChestplate(item) || (Config.ENCHANTMENTS_ITEM_CHESTPLATE_ENCHANTS_TO_ELYTRA.get() && ELYTRA.isIncluded(item));
case LEGGINGS -> ItemUtil.isLeggings(item);
case BOOTS -> ItemUtil.isBoots(item);
case ELYTRA -> item.getType() == Material.ELYTRA;
case WEAPON -> SWORD.isIncluded(item) || ItemUtil.isTrident(item);
case TOOL -> ItemUtil.isTool(item);
case ARMOR -> ItemUtil.isArmor(item);
case SWORD -> ItemUtil.isSword(item) || (Config.ENCHANTMENTS_ITEM_SWORD_ENCHANTS_TO_AXES.get() && AXE.isIncluded(item));
case TRIDENT -> ItemUtil.isTrident(item);
case AXE -> ItemUtil.isAxe(item);
case BOW -> item.getType() == Material.BOW || (Config.ENCHANTMENTS_ITEM_BOW_ENCHANTS_TO_CROSSBOW.get() && 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.api.enchantment;
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,19 @@
package su.nightexpress.excellentenchants.api.enchantment;
public enum Rarity {
COMMON(10),
UNCOMMON(5),
RARE(2),
VERY_RARE(1);
private final int weight;
Rarity(int weight) {
this.weight = weight;
}
public int getWeight() {
return this.weight;
}
}

154
Core/pom.xml Normal file
View File

@ -0,0 +1,154 @@
<?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.6.5</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>
<repository>
<id>dmulloy2-repo</id>
<url>https://repo.dmulloy2.net/repository/public/</url>
</repository>
<repository>
<id>placeholderapi</id>
<url>https://repo.extendedclip.com/content/repositories/placeholderapi/</url>
</repository>
<repository>
<id>lumine-repo</id>
<url>https://mvn.lumine.io/repository/maven-public/</url>
</repository>
<repository>
<id>lumine-snapshot</id>
<url>http://mvn.lumine.io/repository/maven-snapshots/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.20.4-R0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>fr.neatmonster</groupId>
<artifactId>nocheatplus</artifactId>
<version>3.16.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.lumine</groupId>
<artifactId>Mythic-Dist</artifactId>
<version>5.0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib</artifactId>
<version>5.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>me.clip</groupId>
<artifactId>placeholderapi</artifactId>
<version>2.11.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>su.nightexpress.excellentenchants</groupId>
<artifactId>API</artifactId>
<version>3.6.5</version>
</dependency>
<dependency>
<groupId>su.nightexpress.excellentenchants</groupId>
<artifactId>NMS</artifactId>
<version>3.6.5</version>
</dependency>
<dependency>
<groupId>su.nightexpress.excellentenchants</groupId>
<artifactId>V1_18_R2</artifactId>
<version>3.6.5</version>
</dependency>
<dependency>
<groupId>su.nightexpress.excellentenchants</groupId>
<artifactId>V1_19_R3</artifactId>
<version>3.6.5</version>
</dependency>
<dependency>
<groupId>su.nightexpress.excellentenchants</groupId>
<artifactId>V1_20_R1</artifactId>
<version>3.6.5</version>
</dependency>
<dependency>
<groupId>su.nightexpress.excellentenchants</groupId>
<artifactId>V1_20_R2</artifactId>
<version>3.6.5</version>
</dependency>
<dependency>
<groupId>su.nightexpress.excellentenchants</groupId>
<artifactId>V1_20_R3</artifactId>
<version>3.6.5</version>
</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>
<!-- Java 16 Fix -->
<dependencies>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>9.2</version>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-commons</artifactId>
<version>9.2</version>
</dependency>
</dependencies>
<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,172 @@
package su.nightexpress.excellentenchants;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.inventory.ItemStack;
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.command.list.ReloadSubCommand;
import su.nexmedia.engine.utils.EngineUtils;
import su.nightexpress.excellentenchants.api.enchantment.ItemCategory;
import su.nightexpress.excellentenchants.api.enchantment.ObtainType;
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.enchantment.EnchantManager;
import su.nightexpress.excellentenchants.enchantment.EnchantPopulator;
import su.nightexpress.excellentenchants.enchantment.registry.EnchantRegistry;
import su.nightexpress.excellentenchants.hook.HookId;
import su.nightexpress.excellentenchants.hook.impl.PlaceholderHook;
import su.nightexpress.excellentenchants.hook.impl.ProtocolHook;
import su.nightexpress.excellentenchants.nms.EnchantNMS;
import su.nightexpress.excellentenchants.nms.v1_18_R2.V1_18_R2;
import su.nightexpress.excellentenchants.nms.v1_19_R3.V1_19_R3;
import su.nightexpress.excellentenchants.nms.v1_20_R1.V1_20_R1;
import su.nightexpress.excellentenchants.nms.v1_20_R2.V1_20_R2;
import su.nightexpress.excellentenchants.nms.v1_20_R3.V1_20_R3;
import su.nightexpress.excellentenchants.tier.TierManager;
public class ExcellentEnchants extends NexPlugin<ExcellentEnchants> {
// TODO Config option to use minecraft internal enchanting population
// TODO Custom name format for curse enchants
// TODO Custom name format for treasure enchants
private EnchantRegistry registry;
private EnchantManager enchantManager;
private EnchantNMS enchantNMS;
private TierManager tierManager;
@Override
@NotNull
protected ExcellentEnchants getSelf() {
return this;
}
@Override
public void onLoad() {
super.onLoad();
//this.updateFitItemTypes();
this.registry = new EnchantRegistry(this);
}
@Override
public void enable() {
if (!this.setNMS()) {
this.error("Unsupported server version!");
this.getPluginManager().disablePlugin(this);
return;
}
this.tierManager = new TierManager(this);
this.tierManager.setup();
this.registry.setup();
this.enchantManager = new EnchantManager(this);
this.enchantManager.setup();
if (Config.ENCHANTMENTS_DISPLAY_MODE.get() == 2) {
if (EngineUtils.hasPlugin(HookId.PROTOCOL_LIB)) {
ProtocolHook.setup();
}
else {
this.warn(HookId.PROTOCOL_LIB + " is not installed. Set display mode to Plain lore.");
Config.ENCHANTMENTS_DISPLAY_MODE.set(1);
}
}
}
@Override
public void disable() {
if (this.enchantManager != null) {
this.enchantManager.shutdown();
this.enchantManager = null;
}
if (this.tierManager != null) {
this.tierManager.shutdown();
this.tierManager = null;
}
if (EngineUtils.hasPlaceholderAPI()) {
PlaceholderHook.shutdown();
}
this.registry.shutdown();
}
private boolean setNMS() {
this.enchantNMS = switch (Version.getCurrent()) {
case V1_18_R2 -> new V1_18_R2();
case V1_19_R3 -> new V1_19_R3();
case V1_20_R1 -> new V1_20_R1();
case V1_20_R2 -> new V1_20_R2();
case V1_20_R3 -> new V1_20_R3();
default -> null;
};
return this.enchantNMS != null;
}
@Override
public void loadConfig() {
this.getConfig().initializeOptions(Config.class);
}
@Override
public void loadLang() {
this.getLangManager().loadMissing(Lang.class);
this.getLangManager().loadEnum(ItemCategory.class);
this.getLangManager().loadEnum(EnchantmentTarget.class);
this.getLangManager().loadEnum(ObtainType.class);
this.getLang().saveChanges();
}
@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));
mainCommand.addChildren(new ReloadSubCommand<>(this, Perms.COMMAND_RELOAD));
}
@Override
public void registerHooks() {
if (EngineUtils.hasPlaceholderAPI()) {
PlaceholderHook.setup(this);
}
}
@Override
public void registerPermissions() {
this.registerPermissions(Perms.class);
}
@NotNull
public EnchantPopulator createPopulator(@NotNull ItemStack item, @NotNull ObtainType obtainType) {
return new EnchantPopulator(this, item, obtainType);
}
@NotNull
public TierManager getTierManager() {
return tierManager;
}
@NotNull
public EnchantRegistry getRegistry() {
return registry;
}
@NotNull
public EnchantManager getEnchantManager() {
return this.enchantManager;
}
@NotNull
public EnchantNMS getEnchantNMS() {
return enchantNMS;
}
}

View File

@ -0,0 +1,20 @@
package su.nightexpress.excellentenchants;
import org.jetbrains.annotations.NotNull;
import su.nightexpress.excellentenchants.enchantment.EnchantManager;
import su.nightexpress.excellentenchants.tier.TierManager;
public class ExcellentEnchantsAPI {
public static final ExcellentEnchants PLUGIN = ExcellentEnchants.getPlugin(ExcellentEnchants.class);
@NotNull
public static EnchantManager getEnchantManager() {
return PLUGIN.getEnchantManager();
}
@NotNull
public static TierManager getTierManager() {
return PLUGIN.getTierManager();
}
}

View File

@ -0,0 +1,25 @@
package su.nightexpress.excellentenchants;
import org.bukkit.permissions.PermissionDefault;
import su.nexmedia.engine.api.server.JPermission;
public class Perms {
private static final String PREFIX = "excellentenchants.";
private static final String PREFIX_COMMAND = PREFIX + "command.";
public static final JPermission PLUGIN = new JPermission(PREFIX + Placeholders.WILDCARD, "Access to all the plugin functions.");
public static final JPermission COMMAND = new JPermission(PREFIX_COMMAND + Placeholders.WILDCARD, "Access to all the plugin commands.");
public static final JPermission COMMAND_BOOK = new JPermission(PREFIX_COMMAND + "book", "Allows to use '/eenchants book' command.");
public static final JPermission COMMAND_ENCHANT = new JPermission(PREFIX_COMMAND + "enchant", "Allows to use '/eenchants enchant' command.");
public static final JPermission COMMAND_LIST = new JPermission(PREFIX_COMMAND + "list", "Allows to use '/eenchants list' command.", PermissionDefault.TRUE);
public static final JPermission COMMAND_TIERBOOK = new JPermission(PREFIX_COMMAND + "tierbook", "Allows to use '/eenchants tierbook' command.");
public static final JPermission COMMAND_RELOAD = new JPermission(PREFIX_COMMAND + "reload", "Allows to use '/eenchants reload' command.");
static {
PLUGIN.addChildren(COMMAND);
COMMAND.addChildren(COMMAND_BOOK, COMMAND_ENCHANT, COMMAND_LIST, COMMAND_RELOAD, COMMAND_TIERBOOK);
}
}

View File

@ -0,0 +1,54 @@
/*
* Decompiled with CFR 0.151.
*
* Could not load the following classes:
* su.nexmedia.engine.utils.Placeholders
*/
package su.nightexpress.excellentenchants;
public class Placeholders
extends su.nexmedia.engine.utils.Placeholders {
public static final String URL_WIKI = "https://github.com/nulli0n/ExcellentEnchants-spigot/wiki/";
public static final String URL_PLACEHOLDERS = "https://github.com/nulli0n/ExcellentEnchants-spigot/wiki/Internal-Placeholders";
public static final String URL_ENGINE_SCALER = "https://github.com/nulli0n/NexEngine-spigot/wiki/Configuration-Tips#scalable-sections";
public static final String URL_ENGINE_ITEMS = "https://github.com/nulli0n/NexEngine-spigot/wiki/Configuration-Tips#item-sections";
public static final String GENERIC_TYPE = "%type%";
public static final String GENERIC_NAME = "%name%";
public static final String GENERIC_ITEM = "%item%";
public static final String GENERIC_LEVEL = "%level%";
public static final String GENERIC_AMOUNT = "%amount%";
public static final String GENERIC_DESCRIPTION = "%description%";
public static final String GENERIC_ENCHANT = "%enchant%";
public static final String ENCHANTMENT_CHANCE = "%enchantment_trigger_chance%";
public static final String ENCHANTMENT_INTERVAL = "%enchantment_trigger_interval%";
public static final String ENCHANTMENT_POTION_LEVEL = "%enchantment_potion_level%";
public static final String ENCHANTMENT_POTION_DURATION = "%enchantment_potion_duration%";
public static final String ENCHANTMENT_POTION_TYPE = "%enchantment_potion_type%";
public static final String ENCHANTMENT_ID = "%enchantment_id%";
public static final String ENCHANTMENT_NAME = "%enchantment_name%";
public static final String ENCHANTMENT_NAME_FORMATTED = "%enchantment_name_formatted%";
public static final String ENCHANTMENT_DESCRIPTION = "%enchantment_description%";
public static final String ENCHANTMENT_LEVEL = "%enchantment_level%";
public static final String ENCHANTMENT_LEVEL_MIN = "%enchantment_level_min%";
public static final String ENCHANTMENT_LEVEL_MAX = "%enchantment_level_max%";
public static final String ENCHANTMENT_TIER = "%enchantment_tier%";
public static final String ENCHANTMENT_TIER_COLOR = "%enchantment_tier_color%";
public static final String ENCHANTMENT_FIT_ITEM_TYPES = "%enchantment_fit_item_types%";
public static final String ENCHANTMENT_OBTAIN_CHANCE_ENCHANTING = "%enchantment_obtain_chance_enchanting%";
public static final String ENCHANTMENT_OBTAIN_CHANCE_VILLAGER = "%enchantment_obtain_chance_villager%";
public static final String ENCHANTMENT_OBTAIN_CHANCE_LOOT_GENERATION = "%enchantment_obtain_chance_loot_generation%";
public static final String ENCHANTMENT_OBTAIN_CHANCE_FISHING = "%enchantment_obtain_chance_fishing%";
public static final String ENCHANTMENT_OBTAIN_CHANCE_MOB_SPAWNING = "%enchantment_obtain_chance_mob_spawning%";
public static final String ENCHANTMENT_CHARGES_MAX_AMOUNT = "%enchantment_charges_max_amount%";
public static final String ENCHANTMENT_CHARGES_CONSUME_AMOUNT = "%enchantment_charges_consume_amount%";
public static final String ENCHANTMENT_CHARGES_RECHARGE_AMOUNT = "%enchantment_charges_recharge_amount%";
public static final String ENCHANTMENT_CHARGES_FUEL_ITEM = "%enchantment_charges_fuel_item%";
public static final String TIER_ID = "%tier_id%";
public static final String TIER_NAME = "%tier_name%";
public static final String TIER_OBTAIN_CHANCE_ENCHANTING = "%tier_obtain_chance_enchanting%";
public static final String TIER_OBTAIN_CHANCE_VILLAGER = "%tier_obtain_chance_villager%";
public static final String TIER_OBTAIN_CHANCE_LOOT_GENERATION = "%tier_obtain_chance_loot_generation%";
public static final String TIER_OBTAIN_CHANCE_FISHING = "%tier_obtain_chance_fishing%";
public static final String TIER_OBTAIN_CHANCE_MOB_SPAWNING = "%tier_obtain_chance_mob_spawning%";
}

View File

@ -0,0 +1,6 @@
package su.nightexpress.excellentenchants.api.enchantment;
public interface Cleanable {
void clear();
}

View File

@ -0,0 +1,29 @@
package su.nightexpress.excellentenchants.api.enchantment.meta;
import org.bukkit.entity.Projectile;
import org.jetbrains.annotations.NotNull;
import su.nexmedia.engine.utils.values.UniParticle;
import java.util.Optional;
public interface Arrowed {
@NotNull Arrowed getArrowImplementation();
@NotNull
default Optional<UniParticle> getTrailParticle() {
return this.getArrowImplementation().getTrailParticle();
}
default void addTrail(@NotNull Projectile projectile) {
this.getArrowImplementation().addTrail(projectile);
}
default void addData(@NotNull Projectile projectile) {
this.getArrowImplementation().addData(projectile);
}
default boolean isOurProjectile(@NotNull Projectile projectile) {
return this.getArrowImplementation().isOurProjectile(projectile);
}
}

View File

@ -0,0 +1,21 @@
package su.nightexpress.excellentenchants.api.enchantment.meta;
import org.jetbrains.annotations.NotNull;
public interface Chanced {
@NotNull Chanced getChanceImplementation();
/*@NotNull
default UnaryOperator<String> replacePlaceholders(int level) {
return this.getChanceImplementation().replacePlaceholders(level);
}*/
default double getTriggerChance(int level) {
return this.getChanceImplementation().getTriggerChance(level);
}
default boolean checkTriggerChance(int level) {
return getChanceImplementation().checkTriggerChance(level);
}
}

View File

@ -0,0 +1,24 @@
package su.nightexpress.excellentenchants.api.enchantment.meta;
import org.jetbrains.annotations.NotNull;
public interface Periodic {
@NotNull Periodic getPeriodImplementation();
default long getInterval() {
return this.getPeriodImplementation().getInterval();
}
default long getNextTriggerTime() {
return this.getPeriodImplementation().getNextTriggerTime();
}
default boolean isTriggerTime() {
return this.getPeriodImplementation().isTriggerTime();
}
default void updateTriggerTime() {
this.getPeriodImplementation().updateTriggerTime();
}
}

View File

@ -0,0 +1,35 @@
package su.nightexpress.excellentenchants.api.enchantment.meta;
import org.bukkit.entity.LivingEntity;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.jetbrains.annotations.NotNull;
public interface Potioned {
@NotNull Potioned getPotionImplementation();
default boolean isPermanent() {
return this.getPotionImplementation().isPermanent();
}
default PotionEffectType getEffectType() {
return this.getPotionImplementation().getEffectType();
}
default int getEffectAmplifier(int level) {
return this.getPotionImplementation().getEffectAmplifier(level);
}
default int getEffectDuration(int level) {
return this.getPotionImplementation().getEffectDuration(level);
}
default PotionEffect createEffect(int level) {
return this.getPotionImplementation().createEffect(level);
}
default boolean addEffect(@NotNull LivingEntity target, int level) {
return this.getPotionImplementation().addEffect(target, level);
}
}

View File

@ -0,0 +1,18 @@
package su.nightexpress.excellentenchants.api.enchantment.type;
import org.bukkit.entity.LivingEntity;
import org.bukkit.event.EventPriority;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import su.nightexpress.excellentenchants.api.enchantment.IEnchantment;
public interface BlockBreakEnchant extends IEnchantment {
boolean onBreak(@NotNull BlockBreakEvent event, @NotNull LivingEntity player, @NotNull ItemStack item, int level);
@NotNull
default EventPriority getBreakPriority() {
return EventPriority.HIGH;
}
}

View File

@ -0,0 +1,18 @@
package su.nightexpress.excellentenchants.api.enchantment.type;
import org.bukkit.entity.LivingEntity;
import org.bukkit.event.EventPriority;
import org.bukkit.event.block.BlockDropItemEvent;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import su.nightexpress.excellentenchants.api.enchantment.IEnchantment;
public interface BlockDropEnchant extends IEnchantment {
boolean onDrop(@NotNull BlockDropItemEvent event, @NotNull LivingEntity player, @NotNull ItemStack item, int level);
@NotNull
default EventPriority getDropPriority() {
return EventPriority.NORMAL;
}
}

View File

@ -0,0 +1,37 @@
package su.nightexpress.excellentenchants.api.enchantment.type;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Projectile;
import org.bukkit.event.EventPriority;
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.nightexpress.excellentenchants.api.enchantment.IEnchantment;
public interface BowEnchant extends IEnchantment {
boolean onShoot(@NotNull EntityShootBowEvent event, @NotNull LivingEntity shooter, @NotNull ItemStack bow, int level);
boolean onHit(@NotNull ProjectileHitEvent event, LivingEntity user, @NotNull Projectile projectile, @NotNull ItemStack bow, int level);
boolean onDamage(@NotNull EntityDamageByEntityEvent event, @NotNull Projectile projectile,
@NotNull LivingEntity shooter, @NotNull LivingEntity victim,
@NotNull ItemStack weapon, int level);
@NotNull
default EventPriority getShootPriority() {
return EventPriority.NORMAL;
}
@NotNull
default EventPriority getHitPriority() {
return EventPriority.NORMAL;
}
@NotNull
default EventPriority getDamagePriority() {
return EventPriority.NORMAL;
}
}

View File

@ -0,0 +1,29 @@
package su.nightexpress.excellentenchants.api.enchantment.type;
import org.bukkit.entity.LivingEntity;
import org.bukkit.event.EventPriority;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import su.nightexpress.excellentenchants.api.enchantment.IEnchantment;
public interface CombatEnchant extends IEnchantment {
boolean onAttack(@NotNull EntityDamageByEntityEvent event,
@NotNull LivingEntity damager, @NotNull LivingEntity victim,
@NotNull ItemStack weapon, int level);
boolean onProtect(@NotNull EntityDamageByEntityEvent event,
@NotNull LivingEntity damager, @NotNull LivingEntity victim,
@NotNull ItemStack weapon, int level);
@NotNull
default EventPriority getAttackPriority() {
return EventPriority.NORMAL;
}
@NotNull
default EventPriority getProtectPriority() {
return EventPriority.NORMAL;
}
}

View File

@ -0,0 +1,18 @@
package su.nightexpress.excellentenchants.api.enchantment.type;
import org.bukkit.entity.LivingEntity;
import org.bukkit.event.EventPriority;
import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import su.nightexpress.excellentenchants.api.enchantment.IEnchantment;
public interface DamageEnchant extends IEnchantment {
boolean onDamage(@NotNull EntityDamageEvent event, @NotNull LivingEntity entity, @NotNull ItemStack item, int level);
@NotNull
default EventPriority getDamagePriority() {
return EventPriority.NORMAL;
}
}

View File

@ -0,0 +1,29 @@
package su.nightexpress.excellentenchants.api.enchantment.type;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventPriority;
import org.bukkit.event.entity.EntityDeathEvent;
import org.bukkit.event.entity.EntityResurrectEvent;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import su.nightexpress.excellentenchants.api.enchantment.IEnchantment;
public interface DeathEnchant extends IEnchantment {
boolean onDeath(@NotNull EntityDeathEvent event, @NotNull LivingEntity entity, ItemStack item, int level);
boolean onKill(@NotNull EntityDeathEvent event, @NotNull LivingEntity entity, @NotNull Player killer, ItemStack weapon, int level);
boolean onResurrect(@NotNull EntityResurrectEvent event, @NotNull LivingEntity entity, @NotNull ItemStack item, int level);
@NotNull
default EventPriority getDeathPriority() {
return EventPriority.NORMAL;
}
@NotNull
default EventPriority getKillPriority() {
return EventPriority.NORMAL;
}
}

View File

@ -0,0 +1,17 @@
package su.nightexpress.excellentenchants.api.enchantment.type;
import org.bukkit.event.EventPriority;
import org.bukkit.event.player.PlayerFishEvent;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import su.nightexpress.excellentenchants.api.enchantment.IEnchantment;
public interface FishingEnchant extends IEnchantment {
boolean onFishing(@NotNull PlayerFishEvent event, @NotNull ItemStack item, int level);
@NotNull
default EventPriority getFishingPriority() {
return EventPriority.NORMAL;
}
}

View File

@ -0,0 +1,7 @@
package su.nightexpress.excellentenchants.api.enchantment.type;
import su.nightexpress.excellentenchants.api.enchantment.IEnchantment;
public interface GenericEnchant extends IEnchantment {
}

View File

@ -0,0 +1,18 @@
package su.nightexpress.excellentenchants.api.enchantment.type;
import org.bukkit.entity.LivingEntity;
import org.bukkit.event.EventPriority;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import su.nightexpress.excellentenchants.api.enchantment.IEnchantment;
public interface InteractEnchant extends IEnchantment {
boolean onInteract(@NotNull PlayerInteractEvent event, @NotNull LivingEntity entity, @NotNull ItemStack item, int level);
@NotNull
default EventPriority getInteractPriority() {
return EventPriority.NORMAL;
}
}

View File

@ -0,0 +1,12 @@
package su.nightexpress.excellentenchants.api.enchantment.type;
import org.bukkit.entity.LivingEntity;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import su.nightexpress.excellentenchants.api.enchantment.IEnchantment;
import su.nightexpress.excellentenchants.api.enchantment.meta.Periodic;
public interface PassiveEnchant extends IEnchantment, Periodic {
boolean onTrigger(@NotNull LivingEntity entity, @NotNull ItemStack item, int level);
}

View File

@ -0,0 +1,81 @@
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.jetbrains.annotations.NotNull;
import su.nexmedia.engine.api.command.AbstractCommand;
import su.nexmedia.engine.api.command.CommandResult;
import su.nexmedia.engine.utils.CollectionsUtil;
import su.nexmedia.engine.utils.PlayerUtil;
import su.nexmedia.engine.utils.random.Rnd;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Perms;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.config.Lang;
import su.nightexpress.excellentenchants.enchantment.util.EnchantUtils;
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);
this.setDescription(plugin.getMessage(Lang.COMMAND_BOOK_DESC));
this.setUsage(plugin.getMessage(Lang.COMMAND_BOOK_USAGE));
}
@Override
@NotNull
public List<String> getTab(@NotNull Player player, int arg, @NotNull String[] args) {
if (arg == 1) {
return CollectionsUtil.playerNames(player);
}
if (arg == 2) {
return Arrays.stream(Enchantment.values()).map(e -> e.getKey().getKey()).toList();
}
if (arg == 3) {
return Arrays.asList("-1", "1", "5", "10");
}
return super.getTab(player, arg, args);
}
@Override
protected void onExecute(@NotNull CommandSender sender, @NotNull CommandResult result) {
if (result.length() < 4) {
this.printUsage(sender);
return;
}
Player player = PlayerUtil.getPlayer(result.getArg(1));
if (player == null) {
this.errorPlayer(sender);
return;
}
Enchantment enchantment = Enchantment.getByKey(NamespacedKey.minecraft(result.getArg(2).toLowerCase()));
if (enchantment == null) {
plugin.getMessage(Lang.ERROR_NO_ENCHANT).send(sender);
return;
}
int level = result.getInt(3, -1);
if (level < 1) {
level = Rnd.get(enchantment.getStartLevel(), enchantment.getMaxLevel());
}
ItemStack item = new ItemStack(Material.ENCHANTED_BOOK);
EnchantUtils.add(item, enchantment, level, true);
EnchantUtils.updateDisplay(item);
PlayerUtil.addItem(player, item);
plugin.getMessage(Lang.COMMAND_BOOK_DONE)
.replace(Placeholders.GENERIC_ENCHANT, EnchantUtils.getLocalized(enchantment))
.replace(Placeholders.forPlayer(player))
.send(sender);
}
}

View File

@ -0,0 +1,96 @@
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.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import su.nexmedia.engine.api.command.AbstractCommand;
import su.nexmedia.engine.api.command.CommandResult;
import su.nexmedia.engine.utils.*;
import su.nexmedia.engine.utils.random.Rnd;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Perms;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.config.Lang;
import su.nightexpress.excellentenchants.enchantment.util.EnchantUtils;
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);
this.setDescription(plugin.getMessage(Lang.COMMAND_ENCHANT_DESC));
this.setUsage(plugin.getMessage(Lang.COMMAND_ENCHANT_USAGE));
}
@Override
@NotNull
public List<String> getTab(@NotNull Player player, int arg, @NotNull String[] args) {
if (arg == 1) {
return Arrays.stream(Enchantment.values()).map(e -> e.getKey().getKey()).toList();
}
if (arg == 2) {
return Arrays.asList("-1", "1", "5", "10");
}
if (arg == 3) {
return CollectionsUtil.playerNames(player);
}
if (arg == 4) {
return CollectionsUtil.getEnumsList(EquipmentSlot.class);
}
return super.getTab(player, arg, args);
}
@Override
protected void onExecute(@NotNull CommandSender sender, @NotNull CommandResult result) {
if (result.length() < 3) {
this.printUsage(sender);
return;
}
Enchantment enchantment = Enchantment.getByKey(NamespacedKey.minecraft(result.getArg(1).toLowerCase()));
if (enchantment == null) {
plugin.getMessage(Lang.ERROR_NO_ENCHANT).send(sender);
return;
}
Player player = PlayerUtil.getPlayer(result.getArg(3, sender.getName()));
if (player == null) {
this.errorPlayer(sender);
return;
}
EquipmentSlot slot = StringUtil.getEnum(result.getArg(4, ""), EquipmentSlot.class).orElse(EquipmentSlot.HAND);
ItemStack item = player.getInventory().getItem(slot);
if (item == null || item.getType().isAir()) {
this.plugin.getMessage(Lang.COMMAND_ENCHANT_ERROR_NO_ITEM).send(sender);
return;
}
int level = result.getInt(2, -1);
if (level < 0) {
level = Rnd.get(enchantment.getStartLevel(), enchantment.getMaxLevel());
}
if (level > 0) {
EnchantUtils.add(item, enchantment, level, true);
}
else EnchantUtils.remove(item, enchantment);
EnchantUtils.updateDisplay(item);
player.getInventory().setItem(slot, item);
plugin.getMessage(sender == player ? Lang.COMMAND_ENCHANT_DONE_SELF : Lang.COMMAND_ENCHANT_DONE_OTHERS)
.replace(Placeholders.forPlayer(player))
.replace(Placeholders.GENERIC_ITEM, ItemUtil.getItemName(item))
.replace(Placeholders.GENERIC_ENCHANT, EnchantUtils.getLocalized(enchantment))
.replace(Placeholders.GENERIC_LEVEL, NumberUtil.toRoman(level))
.send(sender);
}
}

View File

@ -0,0 +1,24 @@
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.nexmedia.engine.api.command.CommandResult;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Perms;
import su.nightexpress.excellentenchants.config.Lang;
public class ListCommand extends AbstractCommand<ExcellentEnchants> {
public ListCommand(@NotNull ExcellentEnchants plugin) {
super(plugin, new String[]{"list"}, Perms.COMMAND_LIST);
this.setDescription(plugin.getMessage(Lang.COMMAND_LIST_DESC));
this.setPlayerOnly(true);
}
@Override
protected void onExecute(@NotNull CommandSender sender, @NotNull CommandResult result) {
plugin.getEnchantManager().getEnchantsListGUI().open((Player) sender, 1);
}
}

View File

@ -0,0 +1,90 @@
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.api.command.CommandResult;
import su.nexmedia.engine.utils.CollectionsUtil;
import su.nexmedia.engine.utils.Placeholders;
import su.nexmedia.engine.utils.PlayerUtil;
import su.nexmedia.engine.utils.random.Rnd;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Perms;
import su.nightexpress.excellentenchants.config.Lang;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.registry.EnchantRegistry;
import su.nightexpress.excellentenchants.enchantment.util.EnchantUtils;
import su.nightexpress.excellentenchants.tier.Tier;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
public class TierbookCommand extends AbstractCommand<ExcellentEnchants> {
public TierbookCommand(@NotNull ExcellentEnchants plugin) {
super(plugin, new String[]{"tierbook"}, Perms.COMMAND_TIERBOOK);
this.setDescription(plugin.getMessage(Lang.COMMAND_TIER_BOOK_DESC));
this.setUsage(plugin.getMessage(Lang.COMMAND_TIER_BOOK_USAGE));
}
@Override
@NotNull
public List<String> getTab(@NotNull Player player, int arg, @NotNull String[] args) {
if (arg == 1) {
return CollectionsUtil.playerNames(player);
}
if (arg == 2) {
return plugin.getTierManager().getTierIds();
}
if (arg == 3) {
return Arrays.asList("-1", "1", "5", "10");
}
return super.getTab(player, arg, args);
}
@Override
protected void onExecute(@NotNull CommandSender sender, @NotNull CommandResult result) {
if (result.length() < 4) {
this.printUsage(sender);
return;
}
Player player = PlayerUtil.getPlayer(result.getArg(1));
if (player == null) {
this.errorPlayer(sender);
return;
}
Tier tier = plugin.getTierManager().getTierById(result.getArg(2).toLowerCase());
if (tier == null) {
plugin.getMessage(Lang.COMMAND_TIER_BOOK_ERROR).send(sender);
return;
}
Set<ExcellentEnchant> enchants = EnchantRegistry.getOfTier(tier);
ExcellentEnchant enchant = enchants.isEmpty() ? null : Rnd.get(enchants);
if (enchant == null) {
plugin.getMessage(Lang.ERROR_NO_ENCHANT).send(sender);
return;
}
int level = result.getInt(3, -1);
if (level < 1) {
level = Rnd.get(enchant.getStartLevel(), enchant.getMaxLevel());
}
ItemStack item = new ItemStack(Material.ENCHANTED_BOOK);
EnchantUtils.add(item, enchant.getBackend(), level, true);
EnchantUtils.updateDisplay(item);
PlayerUtil.addItem(player, item);
plugin.getMessage(Lang.COMMAND_TIER_BOOK_DONE)
.replace(tier.replacePlaceholders())
.replace(Placeholders.forPlayer(player))
.send(sender);
}
}

View File

@ -0,0 +1,168 @@
package su.nightexpress.excellentenchants.config;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import su.nexmedia.engine.api.config.JOption;
import su.nexmedia.engine.api.config.JYML;
import su.nexmedia.engine.utils.Colorizer;
import su.nexmedia.engine.utils.Colors2;
import su.nexmedia.engine.utils.StringUtil;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.api.enchantment.ObtainType;
import su.nightexpress.excellentenchants.tier.Tier;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Config {
public static final String DIR_MENU = "/menu/";
public static final JOption<Long> TASKS_ARROW_TRAIL_TICKS_INTERVAL = JOption.create("Tasks.Arrow_Trail.Tick_Interval",
1L,
"Sets how often (in ticks) arrow trail particle effects will be spawned behind the arrow."
);
public static final JOption<Long> TASKS_PASSIVE_ENCHANTS_TRIGGER_INTERVAL = JOption.create("Tasks.Passive_Enchants.Trigger_Interval",
100L,
"Sets how often (in ticks) the plugin will attempt to trigger passive enchantment effects on all alive entities.",
"For best results it's recommened to keep this value smaller, but at the same rate as enchantment 'Trigger_Interval' settings.",
"Examples:",
"--- Global: 100 ticks; Regrowth: 100 ticks; Saturation: 100 ticks;",
"--- Global: 50 ticks, Regrowth: 100 ticks; Saturation: 150 ticks;"
);
public static final JOption<Boolean> ENCHANTMENTS_INTERNAL_HANDLER = JOption.create("Enchantments.Internal_Distributor", false,
"[EXPERIMENTAL - DO NOT TOUCH]",
"Sets whether or not enchantment distribution will be handled by vanilla server mechanics.");
public static final JOption<Boolean> ENCHANTMENTS_CHARGES_ENABLED = JOption.create("Enchantments.Charges.Enabled",
false,
"Enables the enchantment Charges feature.",
Placeholders.URL_WIKI + "Charges-System");
public static final JOption<TreeMap<Integer, String>> ENCHANTMENTS_CHARGES_FORMAT = new JOption<TreeMap<Integer, String>>("Enchantments.Charges.Format",
(cfg, path, def) -> {
TreeMap<Integer, String> map = new TreeMap<>();
for (String raw : cfg.getSection(path)) {
int percent = StringUtil.getInteger(raw, -1);
if (percent < 0) continue;
String format = Colorizer.apply(cfg.getString(path + "." + raw, ""));
if (format.isEmpty()) continue;
map.put(percent, format);
}
return map;
},
() -> {
TreeMap<Integer, String> map = new TreeMap<>();
map.put(0, "#ff9a9a(" + Placeholders.GENERIC_AMOUNT + "⚡)");
map.put(25, "#ffc39a(" + Placeholders.GENERIC_AMOUNT + "⚡)");
map.put(50, "#f6ff9a(" + Placeholders.GENERIC_AMOUNT + "⚡)");
map.put(75, "#bcff9a(" + Placeholders.GENERIC_AMOUNT + "⚡)");
return map;
},
"Enchantment charges format depends on amount of charges left (in percent).",
"If you don't want to display charges, leave only keys with negative values.",
"Use '" + Placeholders.GENERIC_AMOUNT + "' placeholder for amount of charges.")
.setWriter((cfg, path, map) -> map.forEach((perc, str) -> cfg.set(path + "." + perc, str)));
public static final JOption<Boolean> ENCHANTMENTS_CHARGES_COMPARE_TYPE_ONLY = JOption.create("Enchantments.Charges.Compare_Material_Only", false,
"When enabled, only item material will be checked to determine if item can be used as an enchantment fuel.",
"When disabled (default), it will compare the whole item meta including name, lore, model data etc.");
public static final JOption<ItemStack> ENCHANTMENTS_CHARGES_FUEL_ITEM = JOption.create("Enchantments.Charges.Fuel_Item",
new ItemStack(Material.LAPIS_LAZULI),
"Default item used to recharge item's enchantments on anvils.",
"If you want different item for certain enchantments, you can do it in that enchantment configs.",
"Item Options: " + Placeholders.URL_ENGINE_SCALER)
.setWriter(JYML::setItem);
public static final JOption<Set<String>> ENCHANTMENTS_DISABLED = JOption.create("Enchantments.Disabled",
Set.of("enchant_name", "other_enchant"),
"A list of enchantments, that will be disabled and removed from the game (server).",
"Enchantment names are the same as enchantment file name in /enchants/ folder. ! Must be in lower_case !",
"Example: To disable 'Explosive Arrows' you need to add 'explosive_arrows' here.")
.mapReader(set -> set.stream().map(String::toLowerCase).collect(Collectors.toSet()));
public static final JOption<Map<String, Set<String>>> ENCHANTMENTS_DISABLED_IN_WORLDS = new JOption<Map<String, Set<String>>>("Enchantments.Disabled_In_Worlds",
(cfg, path, def) -> cfg.getSection(path).stream().collect(Collectors.toMap(k -> k, worldName -> cfg.getStringSet(path + "." + worldName))),
() -> Map.of("your_world_name", Set.of("enchantment_name", "ice_aspect")),
"Here you can disable certain enchantments in certain worlds.",
"Enchantment names are the same as enchantment file name in /enchants/ folder. ! Must be in lower_case !",
"To disable all enchantments for a world, use '" + Placeholders.WILDCARD + "' instead of enchantment names.")
.setWriter((cfg, path, map) -> map.forEach((world, enchants) -> cfg.set(path + "." + world, enchants)));
public static final JOption<Integer> ENCHANTMENTS_DISPLAY_MODE = JOption.create("Enchantments.Display.Mode",
1,
"Sets how enchantment names and descriptions will be handled on items.",
"1 = Plain modification of item's lore (lore changes are real and persistent).",
"2 = Packet modification of item's lore (no real changes are made to the items). Requires ProtocolLib.",
"Plain mode is faster, but may not reflect all changes immediately.",
"Packet mode is slower, but instantly reflect all changes. In creative mode, there is a chance for lore duplication.");
public static final JOption<Boolean> ENCHANTMENTS_DESCRIPTION_ENABLED = JOption.create("Enchantments.Description.Enabled", true,
"When 'true', adds the enchantment description to item lore under enchantment names.",
"For Display-Mode = 2 description is not shown while you're in Creative gamemode.");
public static final JOption<Boolean> ENCHANTMENTS_DESCRIPTION_BOOKS_ONLY = JOption.create("Enchantments.Description.Books_Only",
false,
"Sets whether or not only enchanted books will have enchantment descriptions.");
public static final JOption<String> ENCHANTMENTS_DESCRIPTION_FORMAT = JOption.create("Enchantments.Description.Format",
"&8▸ " + Placeholders.GENERIC_DESCRIPTION,
"Sets the global enchantment description format.").mapReader(Colorizer::apply);
public static final JOption<Integer> ENCHANTMENTS_ITEM_CUSTOM_MAX = JOption.create("Enchantments.Item.Max_Custom_Enchants", 3,
"How many of custom enchantments the item can contain at the same time?");
public static final JOption<Boolean> ENCHANTMENTS_ITEM_SWORD_ENCHANTS_TO_AXES = JOption.create("Enchantments.Item.Sword_Enchants_To_Axes", true,
"Set this to 'true' to allow Sword enchantments for Axes.");
public static final JOption<Boolean> ENCHANTMENTS_ITEM_BOW_ENCHANTS_TO_CROSSBOW = JOption.create("Enchantments.Item.Bow_Enchants_To_Crossbows", true,
"Set this to 'true' to allow Bow enchantments for Crossbows.");
public static final JOption<Boolean> ENCHANTMENTS_ITEM_CHESTPLATE_ENCHANTS_TO_ELYTRA = JOption.create("Enchantments.Item.Chestplate_Enchants_To_Elytra", false,
"Set this to 'true' to allow Chestplate enchantments for Elytras.");
public static final JOption<Boolean> ENCHANTMENTS_ENTITY_PASSIVE_FOR_MOBS = JOption.create("Enchantments.Entity.Apply_Passive_Enchants_To_Mobs", true,
"When enabled, passive enchantments (permanent potion effects, regeneration, etc.) will be applied to mobs as well.",
"Disable this if you're experiencing performance issues.");
public static final JOption<Boolean> ENCHANTMENTS_SINGLE_ENCHANT_IN_VILLAGER_BOOKS = JOption.create("Enchantments.Single_Enchant_In_Villager_Books", true,
"Sets whether or not enchanted books in villager trades will have only 1 enchant, vanilla or custom one.");
private static final JOption<Map<ObtainType, ObtainSettings>> OBTAIN_SETTINGS = new JOption<Map<ObtainType, ObtainSettings>>("Enchantments.Obtaining",
(cfg, path, def) -> Stream.of(ObtainType.values()).collect(Collectors.toMap(k -> k, v -> ObtainSettings.read(cfg, path + "." + v.getPathName()))),
() -> Stream.of(ObtainType.values()).collect(Collectors.toMap(k -> k, v -> new ObtainSettings(true, 4, 80D, 0, 2))),
"Settings for the different ways of obtaining enchantments.")
.setWriter((cfg, path, map) -> map.forEach((type, settings) -> ObtainSettings.write(cfg, path + "." + type.getPathName(), settings)));
@NotNull
public static Optional<ObtainSettings> getObtainSettings(@NotNull ObtainType obtainType) {
ObtainSettings settings = OBTAIN_SETTINGS.get().get(obtainType);
return settings == null || !settings.isEnabled() ? Optional.empty() : Optional.of(settings);
}
@NotNull
public static List<Tier> getDefaultTiers() {
List<Tier> list = new ArrayList<>();
list.add(new Tier("common", 1, "Common", Colors2.WHITE, getObtainWeight(50D)));
list.add(new Tier("rare", 2, "Rare", Colors2.GREEN, getObtainWeight(25D)));
list.add(new Tier("unique", 3, "Unique", Colors2.YELLOW, getObtainWeight(15D)));
list.add(new Tier("legendary", 4, "Legendary", Colors2.ORANGE, getObtainWeight(5D)));
return list;
}
@NotNull
private static Map<ObtainType, Double> getObtainWeight(double weight) {
Map<ObtainType, Double> map = new HashMap<>();
for (ObtainType obtainType : ObtainType.values()) {
map.put(obtainType, weight);
}
return map;
}
}

View File

@ -0,0 +1,30 @@
package su.nightexpress.excellentenchants.config;
import su.nexmedia.engine.api.lang.LangKey;
import su.nexmedia.engine.lang.EngineLang;
import static su.nexmedia.engine.utils.Colors2.*;
import static su.nightexpress.excellentenchants.Placeholders.*;
public class Lang extends EngineLang {
public static final LangKey COMMAND_LIST_DESC = LangKey.of("Command.List.Desc", "List of all custom enchantments.");
public static final LangKey COMMAND_ENCHANT_USAGE = LangKey.of("Command.Enchant.Usage", "<enchant> <level> [player] [slot]");
public static final LangKey COMMAND_ENCHANT_DESC = LangKey.of("Command.Enchant.Desc", "Enchants the item in your hand.");
public static final LangKey COMMAND_ENCHANT_DONE_SELF = LangKey.of("Command.Enchant.Done.Self", LIGHT_ORANGE + GENERIC_ITEM + LIGHT_YELLOW + " enchanted with " + GENERIC_ENCHANT + " " + GENERIC_LEVEL + LIGHT_YELLOW + "!");
public static final LangKey COMMAND_ENCHANT_DONE_OTHERS = LangKey.of("Command.Enchant.Done.Others", LIGHT_ORANGE + PLAYER_DISPLAY_NAME + LIGHT_YELLOW + "'s " + LIGHT_ORANGE + GENERIC_ITEM + LIGHT_YELLOW + " enchanted with " + GENERIC_ENCHANT + " " + GENERIC_LEVEL + LIGHT_YELLOW + "!");
public static final LangKey COMMAND_ENCHANT_ERROR_NO_ITEM = LangKey.of("Command.Enchant.Error.NoItem", RED + "There is no item to enchant!");
public static final LangKey COMMAND_BOOK_USAGE = LangKey.of("Command.Book.Usage", "<player> <enchant> <level>");
public static final LangKey COMMAND_BOOK_DESC = LangKey.of("Command.Book.Desc", "Gives custom enchanted book.");
public static final LangKey COMMAND_BOOK_DONE = LangKey.of("Command.Book.Done", LIGHT_YELLOW + "Given " + LIGHT_ORANGE + GENERIC_ENCHANT + LIGHT_YELLOW + " enchanted book to " + LIGHT_ORANGE + PLAYER_DISPLAY_NAME + LIGHT_YELLOW + ".");
public static final LangKey COMMAND_TIER_BOOK_USAGE = LangKey.of("Command.TierBook.Usage", "<player> <tier> <level>");
public static final LangKey COMMAND_TIER_BOOK_DESC = LangKey.of("Command.TierBook.Desc", "Gives an enchanted book.");
public static final LangKey COMMAND_TIER_BOOK_ERROR = LangKey.of("Command.TierBook.Error", RED + "Invalid tier!");
public static final LangKey COMMAND_TIER_BOOK_DONE = LangKey.of("Command.TierBook.Done", LIGHT_YELLOW + "Given " + LIGHT_ORANGE + TIER_NAME + LIGHT_YELLOW + " enchanted book to " + LIGHT_ORANGE + PLAYER_DISPLAY_NAME + LIGHT_YELLOW + ".");
public static final LangKey ERROR_NO_ENCHANT = LangKey.of("Error.NoEnchant", RED + "Invalid enchantment.");
}

View File

@ -0,0 +1,67 @@
package su.nightexpress.excellentenchants.config;
import org.jetbrains.annotations.NotNull;
import su.nexmedia.engine.api.config.JYML;
public class ObtainSettings {
private final boolean isEnabled;
private final int enchantsTotalMax;
private final double enchantsCustomGenerationChance;
private final int enchantsCustomMin;
private final int enchantsCustomMax;
public ObtainSettings(boolean isEnabled,
int enchantsTotalMax, double enchantsCustomGenerationChance,
int enchantsCustomMin, int enchantsCustomMax) {
this.isEnabled = isEnabled;
this.enchantsTotalMax = enchantsTotalMax;
this.enchantsCustomGenerationChance = enchantsCustomGenerationChance;
this.enchantsCustomMin = enchantsCustomMin;
this.enchantsCustomMax = enchantsCustomMax;
}
@NotNull
public static ObtainSettings read(@NotNull JYML cfg, @NotNull String path) {
boolean isEnabled = cfg.getBoolean(path + ".Enabled");
int enchantsTotalMax = cfg.getInt(path + ".Enchantments.Total_Maximum", 4);
double enchantsCustomGenerationChance = cfg.getDouble(path + ".Enchantments.Custom_Generation_Chance", 50D);
int enchantsCustomMin = cfg.getInt(path + ".Enchantments.Custom_Minimum", 0);
int enchantsCustomMax = cfg.getInt(path + ".Enchantments.Custom_Maximum", 2);
return new ObtainSettings(isEnabled, enchantsTotalMax, enchantsCustomGenerationChance, enchantsCustomMin, enchantsCustomMax);
}
public static void write(@NotNull JYML cfg, @NotNull String path, @NotNull ObtainSettings settings) {
cfg.set(path + ".Enabled", settings.isEnabled());
cfg.set(path + ".Enchantments.Total_Maximum", settings.getEnchantsTotalMax());
cfg.set(path + ".Enchantments.Custom_Generation_Chance", settings.getEnchantsCustomGenerationChance());
cfg.set(path + ".Enchantments.Custom_Minimum", settings.getEnchantsCustomMin());
cfg.set(path + ".Enchantments.Custom_Maximum", settings.getEnchantsCustomMax());
}
public boolean isEnabled() {
return isEnabled;
}
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,63 @@
/*
* Decompiled with CFR 0.151.
*
* Could not load the following classes:
* org.jetbrains.annotations.NotNull
* su.nexmedia.engine.NexPlugin
* su.nexmedia.engine.api.manager.AbstractManager
* su.nexmedia.engine.api.manager.EventListener
*/
package su.nightexpress.excellentenchants.enchantment;
import org.jetbrains.annotations.NotNull;
import su.nexmedia.engine.api.manager.AbstractManager;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.enchantment.listener.EnchantAnvilListener;
import su.nightexpress.excellentenchants.enchantment.listener.EnchantGenericListener;
import su.nightexpress.excellentenchants.enchantment.menu.EnchantmentsListMenu;
import su.nightexpress.excellentenchants.enchantment.task.ArrowTrailsTask;
import su.nightexpress.excellentenchants.enchantment.task.PassiveEnchantsTask;
public class EnchantManager
extends AbstractManager<ExcellentEnchants> {
public static final String DIR_ENCHANTS = "/enchants/";
private EnchantmentsListMenu enchantmentsListMenu;
private ArrowTrailsTask arrowTrailsTask;
private PassiveEnchantsTask passiveEnchantsTask;
public EnchantManager(@NotNull ExcellentEnchants plugin) {
super(plugin);
}
protected void onLoad() {
this.enchantmentsListMenu = new EnchantmentsListMenu(this.plugin);
this.addListener(new EnchantGenericListener(this));
this.addListener(new EnchantAnvilListener(this.plugin));
this.arrowTrailsTask = new ArrowTrailsTask(this.plugin);
this.arrowTrailsTask.start();
this.passiveEnchantsTask = new PassiveEnchantsTask(this.plugin);
this.passiveEnchantsTask.start();
}
protected void onShutdown() {
if (this.enchantmentsListMenu != null) {
this.enchantmentsListMenu.clear();
this.enchantmentsListMenu = null;
}
if (this.arrowTrailsTask != null) {
this.arrowTrailsTask.stop();
this.arrowTrailsTask = null;
}
if (this.passiveEnchantsTask != null) {
this.passiveEnchantsTask.stop();
this.passiveEnchantsTask = null;
}
}
@NotNull
public EnchantmentsListMenu getEnchantsListGUI() {
return this.enchantmentsListMenu;
}
}

View File

@ -0,0 +1,232 @@
package su.nightexpress.excellentenchants.enchantment;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import su.nexmedia.engine.utils.random.Rnd;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.config.Config;
import su.nightexpress.excellentenchants.config.ObtainSettings;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.registry.EnchantRegistry;
import su.nightexpress.excellentenchants.api.enchantment.ObtainType;
import su.nightexpress.excellentenchants.enchantment.util.EnchantUtils;
import su.nightexpress.excellentenchants.tier.Tier;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
import java.util.stream.Collectors;
public class EnchantPopulator {
private final ExcellentEnchants plugin;
private final ObtainType obtainType;
private final ItemStack item;
private final Map<Tier, Set<ExcellentEnchant>> candidates;
private final Map<Enchantment, Integer> population;
private World world;
private Function<ExcellentEnchant, Integer> levelGenerator;
public EnchantPopulator(@NotNull ExcellentEnchants plugin, @NotNull ItemStack item, @NotNull ObtainType obtainType) {
this.plugin = plugin;
this.item = item;
this.obtainType = obtainType;
this.candidates = new HashMap<>();
this.population = new HashMap<>();
this.withLevelGenerator(enchant -> enchant.generateLevel(this.getObtainType()));
this.fillDefaultCandidates();
}
@NotNull
public EnchantPopulator withWorld(@NotNull World world) {
this.world = world;
return this;
}
@NotNull
public EnchantPopulator withLevelGenerator(@NotNull Function<ExcellentEnchant, Integer> levelGenerator) {
this.levelGenerator = levelGenerator;
return this;
}
@NotNull
public EnchantPopulator withDefaultPopulation(@NotNull Map<Enchantment, Integer> population) {
this.getPopulation().putAll(population);
return this;
}
private void fillDefaultCandidates() {
this.plugin.getTierManager().getTiers().forEach(tier -> {
Set<ExcellentEnchant> enchants = EnchantRegistry.getOfTier(tier);
enchants.removeIf(enchant -> {
return !enchant.isObtainable(this.getObtainType()) || (!enchant.getBackend().canEnchantItem(this.getItem()) && !EnchantUtils.isBook(this.getItem()));
});
this.candidates.put(tier, enchants);
});
}
public boolean isEmpty() {
return this.getCandidates().isEmpty() || this.getCandidates().values().stream().allMatch(Set::isEmpty);
}
public boolean isEmpty(@NotNull Tier tier) {
return this.getCandidates(tier).isEmpty();
}
public void purge(@NotNull Tier tier) {
this.getCandidates().remove(tier);
}
public void purge(@NotNull Tier tier, @NotNull ExcellentEnchant enchant) {
this.getCandidates(tier).remove(enchant);
this.getCandidates().keySet().removeIf(this::isEmpty);
}
@NotNull
public ItemStack getItem() {
return item;
}
@NotNull
public ObtainType getObtainType() {
return obtainType;
}
@Nullable
public World getWorld() {
return world;
}
@NotNull
public Function<ExcellentEnchant, Integer> getLevelGenerator() {
return levelGenerator;
}
@NotNull
public Map<Tier, Set<ExcellentEnchant>> getCandidates() {
return this.candidates;
}
@NotNull
public Set<ExcellentEnchant> getCandidates(@NotNull Tier tier) {
return this.getCandidates().getOrDefault(tier, new HashSet<>());
}
@NotNull
public Map<Enchantment, Integer> getPopulation() {
return this.population;
}
@Nullable
public Tier getTierByChance() {
Map<Tier, Double> map = this.getCandidates().keySet().stream()
.filter(tier -> tier.getChance(this.getObtainType()) > 0D)
.collect(Collectors.toMap(k -> k, v -> v.getChance(this.getObtainType()), (o, n) -> n, HashMap::new));
if (map.isEmpty()) return null;
return Rnd.getByWeight(map);
}
@Nullable
public ExcellentEnchant getEnchantByChance(@NotNull Tier tier) {
Map<ExcellentEnchant, Double> map = this.getCandidates(tier).stream()
.collect(Collectors.toMap(k -> k, v -> v.getObtainChance(this.getObtainType())));
return map.isEmpty() ? null : Rnd.getByWeight(map);
}
@NotNull
public Map<Enchantment, Integer> createPopulation() {
Map<Enchantment, Integer> population = this.getPopulation();
ObtainSettings settings = Config.getObtainSettings(this.getObtainType()).orElse(null);
if (settings == null || !Rnd.chance(settings.getEnchantsCustomGenerationChance())) return population;
int enchantsLimit = settings.getEnchantsTotalMax();
int enchantsRolled = Rnd.get(settings.getEnchantsCustomMin(), settings.getEnchantsCustomMax());
// Try to populate as many as possible.
while (!this.isEmpty() && enchantsRolled > 0) {
// Limit reached.
if (population.size() >= enchantsLimit) break;
Tier tier = this.getTierByChance();
if (tier == null) break; // no tiers left.
ExcellentEnchant enchant = this.getEnchantByChance(tier);
// Remove entire tier if no enchants can be selected.
if (enchant == null) {
this.purge(tier);
continue;
}
// Remove disabled world enchants.
if (world != null && enchant.isDisabledInWorld(world)) {
this.purge(tier, enchant);
continue;
}
// Remove conflicting enchants.
if (population.keySet().stream().anyMatch(has -> has.conflictsWith(enchant.getBackend()) || enchant.getBackend().conflictsWith(has))) {
this.purge(tier, enchant);
continue;
}
// Level generation failed.
int level = this.getLevelGenerator().apply(enchant);
if (level < enchant.getStartLevel()) {
this.purge(tier, enchant);
continue;
}
// All good!
this.purge(tier, enchant);
population.put(enchant.getBackend(), level);
enchantsRolled--;
}
return population;
}
public boolean populate() {
ItemStack item = this.getItem();
AtomicBoolean status = new AtomicBoolean(false);
var population = this.getPopulation().isEmpty() ? this.createPopulation() : this.getPopulation();
boolean singleVillagerBook = this.getObtainType() == ObtainType.VILLAGER
&& item.getType() == Material.ENCHANTED_BOOK
&& Config.ENCHANTMENTS_SINGLE_ENCHANT_IN_VILLAGER_BOOKS.get();
if (singleVillagerBook) {
if (!population.isEmpty()) {
EnchantUtils.getAll(item).keySet().forEach(enchantment -> EnchantUtils.remove(item, enchantment));
}
while (population.size() > 1) {
population.remove(Rnd.get(population.keySet()));
}
}
population.forEach((enchantment, level) -> {
if (EnchantUtils.add(item, enchantment, level, false)) {
status.set(true);
}
});
if (status.get()) {
EnchantUtils.updateDisplay(item);
}
return status.get();
}
}

View File

@ -0,0 +1,361 @@
package su.nightexpress.excellentenchants.enchantment.config;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import su.nexmedia.engine.api.config.JOption;
import su.nexmedia.engine.api.config.JYML;
import su.nexmedia.engine.utils.Colorizer;
import su.nexmedia.engine.utils.StringUtil;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.ExcellentEnchantsAPI;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.config.Config;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.api.enchantment.ObtainType;
import su.nightexpress.excellentenchants.tier.Tier;
import su.nightexpress.excellentenchants.tier.TierManager;
import java.util.*;
public class EnchantDefaults {
private final Map<ObtainType, Double> obtainChance;
private final Map<ObtainType, int[]> obtainLevelCap;
private String displayName;
private Tier tier;
private List<String> description;
private boolean hiddenFromList;
private boolean isTreasure;
private boolean tradeable;
private boolean discoverable;
private int levelMin;
private int levelMax;
private int maxMergeLevel;
private EnchantScaler levelByEnchantCost;
private EnchantScaler anvilMergeCost;
private Set<String> conflicts;
private boolean visualEffects;
private boolean chargesEnabled;
private boolean chargesCustomFuel;
private EnchantScaler chargesMax;
private EnchantScaler chargesConsumeAmount;
private EnchantScaler chargesRechargeAmount;
private ItemStack chargesFuel;
public EnchantDefaults(@NotNull ExcellentEnchant enchant) {
this.setDisplayName(StringUtil.capitalizeUnderscored(enchant.getId()));
this.setTier(0.1);
this.setDescription(new ArrayList<>());
this.setHiddenFromList(false);
this.setTreasure(false);
this.setTradeable(true);
this.setDiscoverable(true);
this.setLevelMin(1);
this.setLevelMax(3);
this.setMaxMergeLevel(-1);
this.setConflicts(new HashSet<>());
this.setVisualEffects(true);
this.obtainChance = new HashMap<>();
this.obtainLevelCap = new HashMap<>();
}
public void load(@NotNull ExcellentEnchant enchant) {
ExcellentEnchants plugin = ExcellentEnchantsAPI.PLUGIN;
JYML cfg = enchant.getConfig();
this.setDisplayName(JOption.create("Name", this.getDisplayName(),
"Enchantment display name. It will be shown in item lore.").read(cfg));
Tier tier = plugin.getTierManager().getTierById(JOption.create("Tier", this.getTier().getId(),
"Enchantment tier. Must be a valid tier identifier from the '" + TierManager.FILE_NAME + "'.").read(cfg));
this.setTier(tier == null ? plugin.getTierManager().getMostCommon() : tier);
//this.getTier().getEnchants().add(enchant);
this.setDescription(JOption.create("Description", this.getDescription(),
"Enchantment description. It will be shown in item lore under enchantment name.",
"You can use 'Enchantment' placeholders: " + Placeholders.URL_PLACEHOLDERS)
.read(cfg));
this.setHiddenFromList(JOption.create("Hide_From_List", false,
"Sets whether or not this enchantment will be hidden from Enchants GUI.").read(cfg));
this.setTreasure(JOption.create("Is_Treasure", this.isTreasure(),
"Sets whether this enchantment is a treasure enchantment.",
"Treasure enchantments can only be received via looting, trading, or fishing.").read(cfg));
if (Config.ENCHANTMENTS_INTERNAL_HANDLER.get()) {
this.setTradeable(JOption.create("Tradeable", this.isTradeable(),
"Sets whether or not this enchantment can be populated in villager trades.").read(cfg));
this.setDiscoverable(JOption.create("Discoverable", this.isTradeable(),
"Sets whether or not this enchantment can be populated in enchanting table.").read(cfg));
}
else {
this.setTradeable(false);
this.setDiscoverable(false);
}
this.setLevelMin(JOption.create("Level.Min", this.getLevelMin(),
"Sets the minimal (start) enchantment level. Can not be less than 1.").read(cfg));
this.setLevelMax(JOption.create("Level.Max", this.getLevelMax(),
"Sets the maximal enchantment level. Can not be less than min. level.",
"Note: While you can 'bypass' this value by enchant commands, all level-dependant enchantment",
"settings will have a limit up to this setting.").read(cfg));
this.setMaxMergeLevel(JOption.create("Anvil.Max_Merge_Level", this.getMaxMergeLevel(),
"Sets max. enchantment level that can be obtained by combining 2 items with this enchantment.",
"Set this to '-1' to remove merge limit and just use 'Max Level' instead."
).read(cfg));
this.setLevelByEnchantCost(EnchantScaler.read(enchant, ObtainType.ENCHANTING.getPathName() + ".Level_By_Exp_Cost",
(int)(30D / this.levelMax) + " * " + Placeholders.ENCHANTMENT_LEVEL,
"Sets how much XP levels must be used in enchanting table to obtain this enchantment.",
"With a default formula '9 * %enchantment_level%' it will be [9, 18, 27] XP levels for [1, 2, 3] enchantment levels."));
this.setAnvilMergeCost(EnchantScaler.read(enchant, "Anvil.Merge_Cost", Placeholders.ENCHANTMENT_LEVEL,
"Sets how much XP levels will be added to the anvil cost when combining custom enchantments."));
for (ObtainType obtainType : ObtainType.values()) {
double obtainChance = JOption.create(obtainType.getPathName() + ".Chance", 50D,
"Chance for this enchantment to be obtained via " + obtainType.getPathName()).read(cfg);
this.getObtainChance().put(obtainType, obtainChance);
int levelMin = JOption.create(obtainType.getPathName() + ".Level.Min", -1,
"Minimal level when obtained via " + obtainType.getPathName(),
"Can not be less than enchantment min. level. Set -1 to use enchantment min. level.").read(cfg);
int levelMax = JOption.create(obtainType.getPathName() + ".Level.Max", -1,
"Maximal level when obtained via " + obtainType.getPathName(),
"Can not be greater than enchantment max. level. Set -1 to use enchantment max. level.").read(cfg);
this.getObtainLevelCap().put(obtainType, new int[]{levelMin, levelMax});
}
this.setConflicts(JOption.create("Conflicts", this.getConflicts(),
"A list of conflicting enchantment names.",
"Conflicting enchantments can not be combined on anvils and obtained together on the same item.").read(cfg));
this.setVisualEffects(JOption.create("Settings.Visual_Effects", this.isVisualEffects(),
"Enables/Disables enchantment visual effects, such as particles.").read(cfg));
if (Config.ENCHANTMENTS_CHARGES_ENABLED.get()) {
this.setChargesEnabled(JOption.create("Settings.Charges.Enabled", this.isChargesEnabled(),
"When 'true' enables the Charges system for this enchantment.",
"When enchanted the first time on enchanting table, it will have maximum charges amount.").read(cfg));
this.setChargesCustomFuel(JOption.create("Settings.Charges.Custom_Fuel", this.isChargesCustomFuel(),
"When 'true' uses different (non-default) fuel item (from the 'Fuel_Item' setting) to recharge.").read(cfg));
this.setChargesMax(EnchantScaler.read(enchant, "Settings.Charges.Maximum", "100",
"Maximum amount of charges for the enchantment."));
this.setChargesConsumeAmount(EnchantScaler.read(enchant, "Settings.Charges.Consume_Amount", "1",
"How many charges will be consumed when enchantment is triggered?"));
this.setChargesRechargeAmount(EnchantScaler.read(enchant, "Settings.Charges.Recharge_Amount", "25",
"How many charges will be restored when using 'Fuel Item' in anvil?"));
this.setChargesFuel(JOption.create("Settings.Charges.Fuel_Item", new ItemStack(Material.LAPIS_LAZULI),
"An item, that will be used to restore enchantment charges on anvils.",
"Item Options:" + Placeholders.URL_ENGINE_ITEMS)
.setWriter(JYML::setItem).read(cfg));
}
}
@NotNull
public String getDisplayName() {
return displayName;
}
public void setDisplayName(@NotNull String displayName) {
this.displayName = Colorizer.apply(displayName);
}
@NotNull
public Tier getTier() {
return tier;
}
public void setTier(double rarity) {
this.setTier(ExcellentEnchantsAPI.getTierManager().getByRarityModifier(rarity));
}
public void setTier(@NotNull Tier tier) {
this.tier = tier;
}
public void setDescription(@NotNull String... description) {
this.setDescription(Arrays.asList(description));
}
public void setDescription(@NotNull List<String> description) {
this.description = Colorizer.apply(description);
}
@NotNull
public List<String> getDescription() {
return description;
}
public boolean isHiddenFromList() {
return hiddenFromList;
}
public void setHiddenFromList(boolean hiddenFromList) {
this.hiddenFromList = hiddenFromList;
}
public boolean isTreasure() {
return isTreasure;
}
public void setTreasure(boolean treasure) {
isTreasure = treasure;
}
public boolean isTradeable() {
return tradeable;
}
public void setTradeable(boolean tradeable) {
this.tradeable = tradeable;
}
public boolean isDiscoverable() {
return discoverable;
}
public void setDiscoverable(boolean discoverable) {
this.discoverable = discoverable;
}
public void setLevelMin(int levelMin) {
this.levelMin = Math.max(1, levelMin);
}
public int getLevelMin() {
return levelMin;
}
public void setLevelMax(int levelMax) {
this.levelMax = Math.max(1, levelMax);
}
public int getLevelMax() {
return levelMax;
}
public int getMaxMergeLevel() {
return this.maxMergeLevel;
}
public void setMaxMergeLevel(int maxMergeLevel) {
this.maxMergeLevel = Math.min(this.getLevelMax(), maxMergeLevel);
}
@NotNull
public EnchantScaler getLevelByEnchantCost() {
return levelByEnchantCost;
}
public void setLevelByEnchantCost(@NotNull EnchantScaler levelByEnchantCost) {
this.levelByEnchantCost = levelByEnchantCost;
}
@NotNull
public EnchantScaler getAnvilMergeCost() {
return anvilMergeCost;
}
public void setAnvilMergeCost(@NotNull EnchantScaler anvilMergeCost) {
this.anvilMergeCost = anvilMergeCost;
}
@NotNull
public Map<ObtainType, Double> getObtainChance() {
return obtainChance;
}
@NotNull
public Map<ObtainType, int[]> getObtainLevelCap() {
return obtainLevelCap;
}
public void setConflicts(@NotNull String... conflicts) {
this.setConflicts(new HashSet<>(Arrays.asList(conflicts)));
}
public void setConflicts(@NotNull Set<String> conflicts) {
this.conflicts = conflicts;
}
@NotNull
public Set<String> getConflicts() {
return conflicts;
}
public boolean isVisualEffects() {
return visualEffects;
}
public void setVisualEffects(boolean visualEffects) {
this.visualEffects = visualEffects;
}
public boolean isChargesEnabled() {
return chargesEnabled;
}
public void setChargesEnabled(boolean chargesEnabled) {
this.chargesEnabled = chargesEnabled;
}
public boolean isChargesCustomFuel() {
return chargesCustomFuel;
}
public void setChargesCustomFuel(boolean chargesCustomFuel) {
this.chargesCustomFuel = chargesCustomFuel;
}
@NotNull
public EnchantScaler getChargesMax() {
return chargesMax;
}
public void setChargesMax(@NotNull EnchantScaler chargesMax) {
this.chargesMax = chargesMax;
}
@Nullable
public ItemStack getChargesFuel() {
return chargesFuel;
}
public void setChargesFuel(@Nullable ItemStack chargesFuel) {
this.chargesFuel = chargesFuel;
}
@NotNull
public EnchantScaler getChargesConsumeAmount() {
return chargesConsumeAmount;
}
public void setChargesConsumeAmount(@NotNull EnchantScaler chargesConsumeAmount) {
this.chargesConsumeAmount = chargesConsumeAmount;
}
@NotNull
public EnchantScaler getChargesRechargeAmount() {
return chargesRechargeAmount;
}
public void setChargesRechargeAmount(@NotNull EnchantScaler chargesRechargeAmount) {
this.chargesRechargeAmount = chargesRechargeAmount;
}
}

View File

@ -0,0 +1,80 @@
package su.nightexpress.excellentenchants.enchantment.config;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import su.nexmedia.engine.api.config.JYML;
import su.nexmedia.engine.utils.Evaluator;
import su.nexmedia.engine.utils.StringUtil;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import java.util.*;
public class EnchantScaler {
public EnchantScaler(@NotNull ExcellentEnchant enchant, @NotNull String path) {
this(enchant.getConfig(), path, Placeholders.ENCHANTMENT_LEVEL, enchant.getStartLevel(), enchant.getMaxLevel());
}
@NotNull
public static EnchantScaler read(@NotNull ExcellentEnchant enchant, @NotNull String path, @NotNull String def, @Nullable String... comments) {
enchant.getConfig().addMissing(path, def);
if (comments != null) {
List<String> list = new ArrayList<>(Arrays.asList(comments));
list.add("You can use formulas/expressions here: " + Placeholders.URL_ENGINE_SCALER);
list.add("Level placeholder: " + Placeholders.ENCHANTMENT_LEVEL);
enchant.getConfig().setComments(path, list);
}
return new EnchantScaler(enchant, path);
}
private final int levelMin;
private final int levelMax;
private final TreeMap<Integer, Double> values;
public EnchantScaler(@NotNull JYML cfg, @NotNull String path, @NotNull String levelPlaceholder, int levelMin, int levelMax) {
this.levelMin = levelMin;
this.levelMax = levelMax;
this.values = new TreeMap<>();
// Load different values for each object level.
Set<String> lvlKeys = cfg.getSection(path);
if (!lvlKeys.isEmpty()) {
for (String sLvl : lvlKeys) {
int eLvl = StringUtil.getInteger(sLvl, 0);
if (eLvl < this.getLevelMin() || eLvl > this.getLevelMax()) continue;
String formula = cfg.getString(path + "." + sLvl, "0").replace(levelPlaceholder, sLvl);
values.put(eLvl, Evaluator.evaluate(formula));
}
return;
}
// Load the single formula for all object levels.
for (int lvl = this.getLevelMin(); lvl < (this.getLevelMax() + 1); lvl++) {
String sLvl = String.valueOf(lvl);
String exChance = cfg.getString(path, "").replace(levelPlaceholder, sLvl);
if (exChance.isEmpty()) continue;
values.put(lvl, Evaluator.evaluate(exChance));
}
}
public int getLevelMin() {
return this.levelMin;
}
public int getLevelMax() {
return this.levelMax;
}
@NotNull
public TreeMap<Integer, Double> getValues() {
return this.values;
}
public double getValue(int level) {
Map.Entry<Integer, Double> en = this.values.floorEntry(level);
return en != null ? en.getValue() : 0D;
}
}

View File

@ -0,0 +1,459 @@
package su.nightexpress.excellentenchants.enchantment.impl;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.World;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.LivingEntity;
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.api.manager.EventListener;
import su.nexmedia.engine.api.placeholder.PlaceholderMap;
import su.nexmedia.engine.lang.LangManager;
import su.nexmedia.engine.utils.ItemUtil;
import su.nexmedia.engine.utils.NumberUtil;
import su.nexmedia.engine.utils.random.Rnd;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.api.enchantment.IEnchantment;
import su.nightexpress.excellentenchants.api.enchantment.meta.Chanced;
import su.nightexpress.excellentenchants.api.enchantment.meta.Periodic;
import su.nightexpress.excellentenchants.api.enchantment.meta.Potioned;
import su.nightexpress.excellentenchants.config.Config;
import su.nightexpress.excellentenchants.enchantment.EnchantManager;
import su.nightexpress.excellentenchants.enchantment.config.EnchantDefaults;
import su.nightexpress.excellentenchants.api.enchantment.ItemCategory;
import su.nightexpress.excellentenchants.api.enchantment.ObtainType;
import su.nightexpress.excellentenchants.enchantment.util.EnchantUtils;
import su.nightexpress.excellentenchants.tier.Tier;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Stream;
public abstract class ExcellentEnchant /*extends Enchantment*/ implements IEnchantment {
protected final ExcellentEnchants plugin;
protected final JYML cfg;
protected final String id;
protected final EnchantDefaults defaults;
protected final NamespacedKey chargesKey;
protected final Map<Integer, PlaceholderMap> placeholdersMap;
public ExcellentEnchant(@NotNull ExcellentEnchants plugin, @NotNull String id) {
//super(NamespacedKey.minecraft(id.toLowerCase()));
this.plugin = plugin;
this.id = id.toLowerCase();//this.getKey().getKey();
this.cfg = new JYML(plugin.getDataFolder() + EnchantManager.DIR_ENCHANTS, id + ".yml");
this.chargesKey = new NamespacedKey(plugin, this.getId() + ".charges");
this.defaults = new EnchantDefaults(this);
this.placeholdersMap = new HashMap<>();
}
@NotNull
@Override
public NamespacedKey getKey() {
return EnchantUtils.createKey(this.getId());
}
public void loadSettings() {
this.cfg.reload();
this.placeholdersMap.clear();
this.getDefaults().load(this);
for (int i = this.getStartLevel(); i < this.getMaxLevel() + 1; i++) {
int level = i;
PlaceholderMap map = new PlaceholderMap()
.add(Placeholders.ENCHANTMENT_DESCRIPTION, () -> String.join("\n", this.getDescription()))
.add(Placeholders.ENCHANTMENT_ID, this::getId)
.add(Placeholders.ENCHANTMENT_NAME, this::getDisplayName)
.add(Placeholders.ENCHANTMENT_NAME_FORMATTED, () -> this.getNameFormatted(level))
.add(Placeholders.ENCHANTMENT_LEVEL, () -> NumberUtil.toRoman(level))
.add(Placeholders.ENCHANTMENT_LEVEL_MIN, () -> String.valueOf(this.getStartLevel()))
.add(Placeholders.ENCHANTMENT_LEVEL_MAX, () -> String.valueOf(this.getMaxLevel()))
.add(Placeholders.ENCHANTMENT_TIER, () -> this.getTier().getName())
.add(Placeholders.ENCHANTMENT_TIER_COLOR, () -> this.getTier().getColor())
.add(Placeholders.ENCHANTMENT_FIT_ITEM_TYPES, () -> {
if (this.getFitItemTypes().length == 0) return plugin.getLangManager().getEnum(this.getCategory());
return String.join(", ", Stream.of(this.getFitItemTypes()).map(type -> plugin.getLangManager().getEnum(type)).toList());
})
.add(Placeholders.ENCHANTMENT_OBTAIN_CHANCE_ENCHANTING, () -> NumberUtil.format(this.getObtainChance(ObtainType.ENCHANTING)))
.add(Placeholders.ENCHANTMENT_OBTAIN_CHANCE_VILLAGER, () -> NumberUtil.format(this.getObtainChance(ObtainType.VILLAGER)))
.add(Placeholders.ENCHANTMENT_OBTAIN_CHANCE_LOOT_GENERATION, () -> NumberUtil.format(this.getObtainChance(ObtainType.LOOT_GENERATION)))
.add(Placeholders.ENCHANTMENT_OBTAIN_CHANCE_FISHING, () -> NumberUtil.format(this.getObtainChance(ObtainType.FISHING)))
.add(Placeholders.ENCHANTMENT_OBTAIN_CHANCE_MOB_SPAWNING, () -> NumberUtil.format(this.getObtainChance(ObtainType.MOB_SPAWNING)))
.add(Placeholders.ENCHANTMENT_CHARGES_MAX_AMOUNT, () -> NumberUtil.format(this.getChargesMax(level)))
.add(Placeholders.ENCHANTMENT_CHARGES_CONSUME_AMOUNT, () -> NumberUtil.format(this.getChargesConsumeAmount(level)))
.add(Placeholders.ENCHANTMENT_CHARGES_RECHARGE_AMOUNT, () -> NumberUtil.format(this.getChargesRechargeAmount(level)))
.add(Placeholders.ENCHANTMENT_CHARGES_FUEL_ITEM, () -> ItemUtil.getItemName(this.getChargesFuel()));
if (this instanceof Chanced chanced) {
map.add(Placeholders.ENCHANTMENT_CHANCE, () -> NumberUtil.format(chanced.getTriggerChance(level)));
}
if (this instanceof Periodic periodic) {
map.add(Placeholders.ENCHANTMENT_INTERVAL, () -> NumberUtil.format(periodic.getInterval() / 20D));
}
if (this instanceof Potioned potioned) {
map.add(Placeholders.ENCHANTMENT_POTION_LEVEL, () -> NumberUtil.toRoman(potioned.getEffectAmplifier(level)));
map.add(Placeholders.ENCHANTMENT_POTION_DURATION, () -> NumberUtil.format(potioned.getEffectDuration(level) / 20D));
map.add(Placeholders.ENCHANTMENT_POTION_TYPE, () -> LangManager.getPotionType(potioned.getEffectType()));
}
map.add(this.getTier().getPlaceholders());
this.placeholdersMap.put(level, map);
}
}
@NotNull
public PlaceholderMap getPlaceholders(int level) {
if (level > this.getMaxLevel()) level = this.getMaxLevel();
if (level < this.getStartLevel()) level = this.getStartLevel();
return this.placeholdersMap.get(level);
}
public void addPlaceholder(@NotNull String key, @NotNull Function<Integer, String> replacer) {
for (int level = this.getStartLevel(); level < this.getMaxLevel() + 1; level++) {
this.getPlaceholders(level).add(key, replacer.apply(level));
}
}
public void registerListeners() {
if (this instanceof EventListener listener) {
this.plugin.getPluginManager().registerEvents(listener, plugin);
}
}
@NotNull
public ItemCategory[] getFitItemTypes() {
//FitItemType itemType = FitItemType.getByEnchantmentTarget(this.getCategory());
//return new FitItemType[]{itemType};
return new ItemCategory[0];
}
@Override
public EquipmentSlot[] getSlots() {
return switch (this.getCategory()) {
case BOW, CROSSBOW, TRIDENT, FISHING_ROD, WEAPON, TOOL -> new EquipmentSlot[]{EquipmentSlot.HAND};
case ARMOR_HEAD -> new EquipmentSlot[]{EquipmentSlot.HEAD};
case ARMOR_TORSO -> new EquipmentSlot[]{EquipmentSlot.CHEST};
case ARMOR_LEGS -> new EquipmentSlot[]{EquipmentSlot.LEGS};
case ARMOR_FEET -> new EquipmentSlot[]{EquipmentSlot.FEET};
case ARMOR, WEARABLE -> new EquipmentSlot[]{EquipmentSlot.HEAD, EquipmentSlot.CHEST, EquipmentSlot.LEGS, EquipmentSlot.FEET};
case BREAKABLE -> new EquipmentSlot[]{EquipmentSlot.HAND, EquipmentSlot.HEAD, EquipmentSlot.CHEST, EquipmentSlot.LEGS, EquipmentSlot.FEET};
case VANISHABLE -> EquipmentSlot.values();
default -> throw new IllegalStateException("Unexpected value: " + this.getCategory());
};
/*Set<EquipmentSlot> slots = new HashSet<>();
for (FitItemType itemType : this.getFitItemTypes()) {
slots.addAll(Arrays.asList(itemType.getSlots()));
}
return slots.toArray(new EquipmentSlot[0]);*/
}
public boolean isDisabledInWorld(@NotNull World world) {
Set<String> disabled = Config.ENCHANTMENTS_DISABLED_IN_WORLDS.get().getOrDefault(world.getName(), Collections.emptySet());
return disabled.contains(this.getKey().getKey()) || disabled.contains(Placeholders.WILDCARD);
}
@Override
public boolean isAvailableToUse(@NotNull LivingEntity entity) {
return !this.isDisabledInWorld(entity.getWorld());
}
@NotNull
public JYML getConfig() {
return this.cfg;
}
@NotNull
public String getId() {
return this.id;
}
@NotNull
public EnchantDefaults getDefaults() {
return defaults;
}
/*@NotNull
@Override
public String getName() {
return getId().toUpperCase();
}*/
@NotNull
public String getDisplayName() {
return this.getDefaults().getDisplayName();
}
@NotNull
public String getNameFormatted(int level) {
String name = this.getTier().getColor() + this.getDisplayName();
if (level > 1 || this.getMaxLevel() > 1) {
name += " " + NumberUtil.toRoman(level);
}
return name;
}
@NotNull
public String getNameFormatted(int level, int charges) {
if (!this.isChargesEnabled() || charges < 0) return this.getNameFormatted(level);
int chargesMax = this.getChargesMax(level);
int percent = (int) Math.ceil((double) charges / (double) chargesMax * 100D);
Map.Entry<Integer, String> entry = Config.ENCHANTMENTS_CHARGES_FORMAT.get().floorEntry(percent);
if (entry == null) return this.getNameFormatted(level);
String format = entry.getValue().replace(Placeholders.GENERIC_AMOUNT, String.valueOf(charges));
return this.getNameFormatted(level) + " " + format;
}
@NotNull
public List<String> getDescription() {
return this.getDefaults().getDescription();
}
@NotNull
public List<String> getDescription(int level) {
List<String> description = new ArrayList<>(this.getDescription());
description.replaceAll(this.getPlaceholders(level).replacer());
return description;
}
@NotNull
public List<String> formatDescription() {
return new ArrayList<>(this.getDescription().stream()
.map(line -> Config.ENCHANTMENTS_DESCRIPTION_FORMAT.get().replace(Placeholders.GENERIC_DESCRIPTION, line))
.toList());
}
@NotNull
public List<String> formatDescription(int level) {
return new ArrayList<>(this.getDescription(level).stream()
.map(line -> Config.ENCHANTMENTS_DESCRIPTION_FORMAT.get().replace(Placeholders.GENERIC_DESCRIPTION, line))
.toList());
}
public boolean hasConflicts() {
return !this.getConflicts().isEmpty();
}
@NotNull
public Set<String> getConflicts() {
return this.getDefaults().getConflicts();
}
@NotNull
public Tier getTier() {
return this.getDefaults().getTier();
}
@Override
public int getMaxLevel() {
return this.getDefaults().getLevelMax();
}
@Override
public int getStartLevel() {
return this.getDefaults().getLevelMin();
}
public int getMaxMergeLevel() {
return this.getDefaults().getMaxMergeLevel() < 0 ? this.getMaxLevel() : this.getDefaults().getMaxMergeLevel();
}
public int getLevelByEnchantCost(int expLevel) {
int get = this.getDefaults().getLevelByEnchantCost().getValues().entrySet().stream()
.filter(en -> expLevel >= en.getValue().intValue()).max(Comparator.comparingInt(Map.Entry::getKey))
.map(Map.Entry::getKey).orElse(0);
return get != 0 ? this.fineLevel(get, ObtainType.ENCHANTING) : 0;
}
public boolean isObtainable(@NotNull ObtainType obtainType) {
if (obtainType == ObtainType.ENCHANTING && (this.isTreasure() || this.isCurse())) return false;
return this.getObtainChance(obtainType) > 0D;
}
public double getObtainChance(@NotNull ObtainType obtainType) {
return this.getDefaults().getObtainChance().getOrDefault(obtainType, 0D);
}
public int getObtainLevelMin(@NotNull ObtainType obtainType) {
return this.getDefaults().getObtainLevelCap().getOrDefault(obtainType, new int[]{-1, -1})[0];
}
public int getObtainLevelMax(@NotNull ObtainType obtainType) {
return this.getDefaults().getObtainLevelCap().getOrDefault(obtainType, new int[]{-1, -1})[1];
}
public int fineLevel(int level, @NotNull ObtainType obtainType) {
int levelCapMin = this.getObtainLevelMin(obtainType);
int levelCapMax = this.getObtainLevelMax(obtainType);
if (levelCapMin > 0 && level < levelCapMin) level = levelCapMin;
if (levelCapMax > 0 && level > levelCapMax) level = levelCapMax;
return level;
}
public int generateLevel() {
return Rnd.get(this.getStartLevel(), this.getMaxLevel());
}
public int generateLevel(@NotNull ObtainType obtainType) {
int levelCapMin = this.getObtainLevelMin(obtainType);
int levelCapMax = this.getObtainLevelMax(obtainType);
if (levelCapMin <= 0 || levelCapMin < this.getStartLevel()) levelCapMin = this.getStartLevel();
if (levelCapMax <= 0 || levelCapMax > this.getMaxLevel()) levelCapMax = this.getMaxLevel();
return Rnd.get(levelCapMin, levelCapMax);
}
public int getAnvilMergeCost(int level) {
return (int) this.getDefaults().getAnvilMergeCost().getValue(level);
}
/*@Override
@Deprecated
public final boolean conflictsWith(@NotNull Enchantment enchantment) {
return this.getConflicts().contains(enchantment.getKey().getKey());
}*/
@Deprecated
public Enchantment getBackend() {
return EnchantUtils.getEnchantment(this.getKey());
}
@Override
public final boolean checkEnchantCategory(@NotNull ItemStack item) {
EnchantmentTarget category = this.getCategory();
if (category == EnchantmentTarget.WEAPON && ItemUtil.isAxe(item)) {
return Config.ENCHANTMENTS_ITEM_SWORD_ENCHANTS_TO_AXES.get();
}
if (category == EnchantmentTarget.BOW && item.getType() == Material.CROSSBOW) {
return Config.ENCHANTMENTS_ITEM_BOW_ENCHANTS_TO_CROSSBOW.get();
}
if ((category == EnchantmentTarget.ARMOR || category == EnchantmentTarget.ARMOR_TORSO) && item.getType() == Material.ELYTRA) {
return Config.ENCHANTMENTS_ITEM_CHESTPLATE_ENCHANTS_TO_ELYTRA.get();
}
return false;
}
@Override
public boolean checkItemCategory(@NotNull ItemStack item) {
ItemCategory[] itemCategories = this.getFitItemTypes();
if (itemCategories.length == 0) return false;
return Stream.of(itemCategories).anyMatch(itemCategory -> itemCategory.isIncluded(item));
}
@Override
public boolean isCurse() {
return false;
}
@Override
public final boolean isTreasure() {
return this.getDefaults().isTreasure();
}
@Override
public boolean isTradeable() {
return this.getDefaults().isTradeable();
}
@Override
public boolean isDiscoverable() {
return this.getDefaults().isDiscoverable();
}
public boolean hasVisualEffects() {
return this.getDefaults().isVisualEffects();
}
public boolean isChargesEnabled() {
return Config.ENCHANTMENTS_CHARGES_ENABLED.get() && this.getDefaults().isChargesEnabled();
}
public boolean isChargesCustomFuel() {
return this.getDefaults().isChargesCustomFuel();
}
public int getChargesMax(int level) {
return this.isChargesEnabled() ? (int) this.getDefaults().getChargesMax().getValue(level) : 0;
}
public int getChargesConsumeAmount(int level) {
return this.isChargesEnabled() ? (int) this.getDefaults().getChargesConsumeAmount().getValue(level) : 0;
}
public int getChargesRechargeAmount(int level) {
return this.isChargesEnabled() ? (int) this.getDefaults().getChargesRechargeAmount().getValue(level) : 0;
}
@NotNull
public ItemStack getChargesFuel() {
ItemStack fuelHas = this.getDefaults().getChargesFuel();
if (!this.isChargesCustomFuel() || fuelHas == null || fuelHas.getType().isAir()) {
return Config.ENCHANTMENTS_CHARGES_FUEL_ITEM.get();
}
return new ItemStack(fuelHas);
}
public boolean isChargesFuel(@NotNull ItemStack item) {
if (Config.ENCHANTMENTS_CHARGES_COMPARE_TYPE_ONLY.get()) {
return item.getType() == this.getChargesFuel().getType();
}
return item.isSimilar(this.getChargesFuel());
}
@NotNull
public NamespacedKey getChargesKey() {
return chargesKey;
}
@Override
public boolean isOutOfCharges(@NotNull ItemStack item) {
return EnchantUtils.isOutOfCharges(item, this);
}
@Override
public boolean isFullOfCharges(@NotNull ItemStack item) {
return EnchantUtils.isFullOfCharges(item, this);
}
@Override
public int getCharges(@NotNull ItemStack item) {
return EnchantUtils.getCharges(item, this);
}
@Override
public void consumeChargesNoUpdate(@NotNull ItemStack item, int level) {
if (!this.isChargesEnabled()) return;
EnchantUtils.consumeCharges(item, this, level);
}
@Override
public void consumeCharges(@NotNull ItemStack item, int level) {
if (!this.isChargesEnabled()) return;
this.consumeChargesNoUpdate(item, level);
EnchantUtils.updateDisplay(item);
}
}

View File

@ -0,0 +1,59 @@
package su.nightexpress.excellentenchants.enchantment.impl.armor;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.LivingEntity;
import org.bukkit.inventory.ItemStack;
import org.bukkit.potion.PotionEffectType;
import org.jetbrains.annotations.NotNull;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.api.enchantment.meta.Potioned;
import su.nightexpress.excellentenchants.api.enchantment.type.PassiveEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.meta.PeriodImplementation;
import su.nightexpress.excellentenchants.enchantment.impl.meta.PotionImplementation;
public class AquamanEnchant extends ExcellentEnchant implements Potioned, PassiveEnchant {
public static final String ID = "aquaman";
private PotionImplementation potionImplementation;
private PeriodImplementation periodImplementation;
public AquamanEnchant(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setLevelMax(1);
this.getDefaults().setTier(0.4);
this.getDefaults().setDescription("Grants permanent " + Placeholders.ENCHANTMENT_POTION_TYPE + " " + Placeholders.ENCHANTMENT_POTION_LEVEL + " effect.");
}
@Override
public void loadSettings() {
super.loadSettings();
this.potionImplementation = PotionImplementation.create(this, PotionEffectType.WATER_BREATHING, true);
this.periodImplementation = PeriodImplementation.create(this, "100");
}
@NotNull
@Override
public PotionImplementation getPotionImplementation() {
return potionImplementation;
}
@NotNull
@Override
public PeriodImplementation getPeriodImplementation() {
return periodImplementation;
}
@Override
@NotNull
public EnchantmentTarget getCategory() {
return EnchantmentTarget.ARMOR_HEAD;
}
@Override
public boolean onTrigger(@NotNull LivingEntity entity, @NotNull ItemStack item, int level) {
return this.addEffect(entity, level);
}
}

View File

@ -0,0 +1,80 @@
package su.nightexpress.excellentenchants.enchantment.impl.armor;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.LivingEntity;
import org.bukkit.event.EventPriority;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.potion.PotionEffectType;
import org.jetbrains.annotations.NotNull;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.api.enchantment.meta.Chanced;
import su.nightexpress.excellentenchants.api.enchantment.meta.Potioned;
import su.nightexpress.excellentenchants.api.enchantment.type.CombatEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.meta.ChanceImplementation;
import su.nightexpress.excellentenchants.enchantment.impl.meta.PotionImplementation;
public class ColdSteelEnchant extends ExcellentEnchant implements Chanced, Potioned, CombatEnchant {
public static final String ID = "cold_steel";
private ChanceImplementation chanceImplementation;
private PotionImplementation potionImplementation;
public ColdSteelEnchant(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription(Placeholders.ENCHANTMENT_CHANCE + "% chance to apply " + Placeholders.ENCHANTMENT_POTION_TYPE + " " + Placeholders.ENCHANTMENT_POTION_LEVEL + " (" + Placeholders.ENCHANTMENT_POTION_DURATION + "s.) on attacker.");
this.getDefaults().setTier(0.3);
this.getDefaults().setLevelMax(3);
}
@Override
public void loadSettings() {
super.loadSettings();
this.chanceImplementation = ChanceImplementation.create(this,
"60 + " + Placeholders.ENCHANTMENT_LEVEL + " * 5");
this.potionImplementation = PotionImplementation.create(this, PotionEffectType.SLOW_DIGGING, false,
"4 + " + Placeholders.ENCHANTMENT_LEVEL,
Placeholders.ENCHANTMENT_LEVEL);
}
@NotNull
@Override
public ChanceImplementation getChanceImplementation() {
return chanceImplementation;
}
@NotNull
@Override
public PotionImplementation getPotionImplementation() {
return potionImplementation;
}
@Override
@NotNull
public EnchantmentTarget getCategory() {
return EnchantmentTarget.ARMOR_TORSO;
}
@NotNull
@Override
public EventPriority getProtectPriority() {
return EventPriority.HIGHEST;
}
@Override
public boolean onAttack(@NotNull EntityDamageByEntityEvent event, @NotNull LivingEntity damager, @NotNull LivingEntity victim, @NotNull ItemStack weapon, int level) {
return false;
}
@Override
public boolean onProtect(@NotNull EntityDamageByEntityEvent event, @NotNull LivingEntity damager, @NotNull LivingEntity victim, @NotNull ItemStack weapon, int level) {
if (!this.checkTriggerChance(level)) return false;
return this.addEffect(damager, level);
}
}

View File

@ -0,0 +1,86 @@
package su.nightexpress.excellentenchants.enchantment.impl.armor;
import org.bukkit.Particle;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.LivingEntity;
import org.bukkit.event.EventPriority;
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.utils.values.UniParticle;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.api.enchantment.meta.Chanced;
import su.nightexpress.excellentenchants.api.enchantment.meta.Potioned;
import su.nightexpress.excellentenchants.api.enchantment.type.CombatEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.meta.ChanceImplementation;
import su.nightexpress.excellentenchants.enchantment.impl.meta.PotionImplementation;
public class DarknessCloakEnchant extends ExcellentEnchant implements Chanced, Potioned, CombatEnchant {
public static final String ID = "darkness_cloak";
private ChanceImplementation chanceImplementation;
private PotionImplementation potionImplementation;
public DarknessCloakEnchant(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription(Placeholders.ENCHANTMENT_CHANCE + "% chance to apply " + Placeholders.ENCHANTMENT_POTION_TYPE + " " + Placeholders.ENCHANTMENT_POTION_LEVEL + " (" + Placeholders.ENCHANTMENT_POTION_DURATION + "s.) on attacker.");
this.getDefaults().setLevelMax(3);
this.getDefaults().setTier(0.2);
}
@Override
public void loadSettings() {
super.loadSettings();
this.chanceImplementation = ChanceImplementation.create(this,
"20.0 * " + Placeholders.ENCHANTMENT_LEVEL + " * 0.75");
this.potionImplementation = PotionImplementation.create(this, PotionEffectType.DARKNESS, false,
"2.5" + Placeholders.ENCHANTMENT_LEVEL,
Placeholders.ENCHANTMENT_LEVEL);
}
@NotNull
@Override
public ChanceImplementation getChanceImplementation() {
return chanceImplementation;
}
@NotNull
@Override
public PotionImplementation getPotionImplementation() {
return potionImplementation;
}
@Override
@NotNull
public EnchantmentTarget getCategory() {
return EnchantmentTarget.ARMOR_TORSO;
}
@NotNull
@Override
public EventPriority getProtectPriority() {
return EventPriority.HIGHEST;
}
@Override
public boolean onAttack(@NotNull EntityDamageByEntityEvent event, @NotNull LivingEntity damager, @NotNull LivingEntity victim, @NotNull ItemStack weapon, int level) {
return false;
}
@Override
public boolean onProtect(@NotNull EntityDamageByEntityEvent event, @NotNull LivingEntity damager, @NotNull LivingEntity victim, @NotNull ItemStack weapon, int level) {
if (!this.checkTriggerChance(level)) return false;
if (!this.addEffect(damager, level)) return false;
if (this.hasVisualEffects()) {
UniParticle.of(Particle.ASH).play(damager.getEyeLocation(), 0.75, 0.1, 30);
}
return true;
}
}

View File

@ -0,0 +1,106 @@
package su.nightexpress.excellentenchants.enchantment.impl.armor;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.LivingEntity;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import su.nexmedia.engine.api.config.JOption;
import su.nexmedia.engine.api.manager.EventListener;
import su.nexmedia.engine.utils.NumberUtil;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.api.enchantment.type.GenericEnchant;
import su.nightexpress.excellentenchants.enchantment.config.EnchantScaler;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.util.EnchantUtils;
import java.util.Set;
public class ElementalProtectionEnchant extends ExcellentEnchant implements GenericEnchant, EventListener {
public static final String ID = "elemental_protection";
public static final String PLACEHOLDER_PROTECTION_AMOUNT = "%enchantment_protection_amount%";
public static final String PLACEHOLDER_PROTECTION_CAPACITY = "%enchantment_protection_capacity%";
private static final Set<EntityDamageEvent.DamageCause> DAMAGE_CAUSES = Set.of(
EntityDamageEvent.DamageCause.POISON, EntityDamageEvent.DamageCause.WITHER,
EntityDamageEvent.DamageCause.MAGIC, EntityDamageEvent.DamageCause.FREEZE,
EntityDamageEvent.DamageCause.LIGHTNING);
private EnchantScaler protectionAmount;
private double protectionCapacity;
private boolean protectionAsModifier;
public ElementalProtectionEnchant(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription("Reduces Poison, Magic, Wither, Lightning, Freeze damage by " + PLACEHOLDER_PROTECTION_AMOUNT + ".");
this.getDefaults().setLevelMax(5);
this.getDefaults().setTier(0.2);
}
@Override
public void loadSettings() {
super.loadSettings();
this.protectionAmount = EnchantScaler.read(this, "Settings.Protection.Amount",
"0.05 * " + Placeholders.ENCHANTMENT_LEVEL,
"How protection the enchantment will have?");
this.protectionCapacity = JOption.create("Settings.Protection.Capacity", 1D,
"Maximal possible protection value from all armor pieces together.").read(cfg);
this.protectionAsModifier = JOption.create("Settings.Protection.As_Modifier", false,
"When 'true' damage will be reduced by a percent of protection value.",
"When 'false' damage will be reduced by a plain protection value.").read(cfg);
this.addPlaceholder(PLACEHOLDER_PROTECTION_AMOUNT, level -> NumberUtil.format(this.getProtectionAmount(level)));
this.addPlaceholder(PLACEHOLDER_PROTECTION_CAPACITY, level -> NumberUtil.format(this.getProtectionCapacity()));
}
@NotNull
@Override
public EnchantmentTarget getCategory() {
return EnchantmentTarget.ARMOR;
}
public double getProtectionAmount(int level) {
return this.protectionAmount.getValue(level);
}
public double getProtectionCapacity() {
return this.protectionCapacity;
}
public boolean isProtectionAsModifier() {
return this.protectionAsModifier;
}
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onDamage(EntityDamageEvent event) {
if (!DAMAGE_CAUSES.contains(event.getCause())) return;
if (!(event.getEntity() instanceof LivingEntity entity)) return;
if (!this.isAvailableToUse(entity)) return;
double protectionAmount = 0D;
for (ItemStack armor : EnchantUtils.getEnchantedEquipment(entity).values()) {
int level = EnchantUtils.getLevel(armor, this.getBackend());
if (level <= 0) continue;
protectionAmount += this.getProtectionAmount(level);
this.consumeCharges(armor, level);
}
if (protectionAmount <= 0D) return;
if (protectionAmount > this.getProtectionCapacity()) {
protectionAmount = this.getProtectionCapacity();
}
if (this.isProtectionAsModifier()) {
event.setDamage(Math.max(0, event.getDamage() * (1D - protectionAmount)));
}
else {
event.setDamage(Math.max(0, event.getDamage() - protectionAmount));
}
}
}

View File

@ -0,0 +1,88 @@
package su.nightexpress.excellentenchants.enchantment.impl.armor;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.LivingEntity;
import org.bukkit.event.EventPriority;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import su.nexmedia.engine.utils.NumberUtil;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.api.enchantment.meta.Chanced;
import su.nightexpress.excellentenchants.api.enchantment.type.CombatEnchant;
import su.nightexpress.excellentenchants.enchantment.config.EnchantScaler;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.meta.ChanceImplementation;
public class FireShieldEnchant extends ExcellentEnchant implements Chanced, CombatEnchant {
public static final String ID = "fire_shield";
public static final String PLACEHOLDER_FIRE_DURATION = "%enchantment_fire_duration%";
private EnchantScaler fireDuration;
private ChanceImplementation chanceImplementation;
public FireShieldEnchant(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription(Placeholders.ENCHANTMENT_CHANCE + "% chance to ignite the attacker for " + PLACEHOLDER_FIRE_DURATION + "s.");
this.getDefaults().setLevelMax(3);
this.getDefaults().setTier(0.4);
}
@Override
public void loadSettings() {
super.loadSettings();
this.chanceImplementation = ChanceImplementation.create(this,
Placeholders.ENCHANTMENT_LEVEL + " * 15.0");
this.fireDuration = EnchantScaler.read(this, "Settings.Fire.Duration",
"2.5 * " + Placeholders.ENCHANTMENT_LEVEL,
"Sets the fire duration (in seconds).",
"If entity's current fire ticks amount is less than this value, it will be set to this value.",
"If entity's current fire ticks amount is greater than this value, it won't be changed.");
this.addPlaceholder(PLACEHOLDER_FIRE_DURATION, level -> NumberUtil.format(this.getFireDuration(level)));
}
@NotNull
@Override
public ChanceImplementation getChanceImplementation() {
return chanceImplementation;
}
@NotNull
@Override
public EnchantmentTarget getCategory() {
return EnchantmentTarget.ARMOR;
}
@NotNull
@Override
public EventPriority getProtectPriority() {
return EventPriority.HIGHEST;
}
public double getFireDuration(int level) {
return this.fireDuration.getValue(level);
}
@Override
public boolean onAttack(@NotNull EntityDamageByEntityEvent event, @NotNull LivingEntity damager, @NotNull LivingEntity victim, @NotNull ItemStack weapon, int level) {
return false;
}
@Override
public boolean onProtect(@NotNull EntityDamageByEntityEvent event,
@NotNull LivingEntity damager, @NotNull LivingEntity victim,
@NotNull ItemStack weapon, int level) {
if (!this.checkTriggerChance(level)) return false;
int ticksToSet = (int) (this.getFireDuration(level) * 20);
int ticksHas = damager.getFireTicks();
if (ticksHas >= ticksToSet) return false;
damager.setFireTicks(ticksToSet);
return true;
}
}

View File

@ -0,0 +1,210 @@
package su.nightexpress.excellentenchants.enchantment.impl.armor;
import org.bukkit.Location;
import org.bukkit.Material;
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.block.BlockExplodeEvent;
import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.event.entity.EntityExplodeEvent;
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.Version;
import su.nexmedia.engine.api.manager.EventListener;
import su.nexmedia.engine.api.server.AbstractTask;
import su.nexmedia.engine.utils.Pair;
import su.nexmedia.engine.utils.random.Rnd;
import su.nexmedia.engine.utils.values.UniParticle;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.api.enchantment.Cleanable;
import su.nightexpress.excellentenchants.api.enchantment.type.GenericEnchant;
import su.nightexpress.excellentenchants.enchantment.config.EnchantScaler;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.util.EnchantUtils;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
public class FlameWalkerEnchant extends ExcellentEnchant implements GenericEnchant, EventListener, Cleanable {
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, Pair<Long, Integer>> BLOCKS_TO_DESTROY = new ConcurrentHashMap<>();
private EnchantScaler blockDecayTime;
private BlockTickTask blockTickTask;
public FlameWalkerEnchant(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription("Ability to walk on lava and magma blocks without getting damage.");
this.getDefaults().setLevelMax(3);
this.getDefaults().setTier(0.7);
this.getDefaults().getConflicts().add(Enchantment.FROST_WALKER.getKey().getKey());
this.blockTickTask = new BlockTickTask(plugin);
this.blockTickTask.start();
}
@Override
public void loadSettings() {
super.loadSettings();
this.blockDecayTime = EnchantScaler.read(this, "Settings.Block_Decay", "12.0",
"Sets up to how long (in seconds) blocks will stay before turn back to lava.");
}
@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, Pair.of(System.currentTimeMillis() + (long) seconds * 1000L, Rnd.get(100)));
}
public static boolean isBlock(@NotNull Block block) {
return BLOCKS_TO_DESTROY.containsKey(block);
}
@Override
@NotNull
public EnchantmentTarget getCategory() {
return EnchantmentTarget.ARMOR_FEET;
}
public double getBlockDecayTime(int level) {
return this.blockDecayTime.getValue(level);
}
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
public void onPlayerMove(PlayerMoveEvent event) {
Player player = event.getPlayer();
if (player.isFlying() || !this.isAvailableToUse(player)) return;
Location from = event.getFrom();
Location to = event.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 = EnchantUtils.getLevel(boots, this.getBackend());
if (level <= 0) return;
Block bTo = to.getBlock().getRelative(BlockFace.DOWN);
boolean hasLava = Stream.of(FACES).anyMatch(face -> bTo.getRelative(face).getType() == Material.LAVA);
if (!hasLava) return;
plugin.getEnchantNMS().handleFlameWalker(player, player.getLocation(), level).forEach(block -> {
addBlock(block, Rnd.getDouble(this.getBlockDecayTime(level)) + 1);
});
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onFlameWalkerBlock(BlockBreakEvent event) {
if (isBlock(event.getBlock())) {
event.setDropItems(false);
event.setExpToDrop(0);
event.getBlock().setType(Material.LAVA);
}
}
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onBlockExplode(EntityExplodeEvent event) {
this.processExplosion(event.blockList());
}
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onBlockExplode2(BlockExplodeEvent event) {
this.processExplosion(event.blockList());
}
private void processExplosion(@NotNull List<Block> blocks) {
blocks.removeIf(block -> {
if (isBlock(block)) {
block.setType(Material.AIR);
return true;
}
return false;
});
}
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onMagmaDamage(EntityDamageEvent event) {
if (event.getCause() != EntityDamageEvent.DamageCause.HOT_FLOOR) return;
if (!(event.getEntity() instanceof LivingEntity livingEntity)) return;
if (!this.isAvailableToUse(livingEntity)) return;
EntityEquipment equipment = livingEntity.getEquipment();
if (equipment == null) return;
ItemStack boots = equipment.getBoots();
if (boots == null || boots.getType().isAir()) return;
int level = EnchantUtils.getLevel(boots, this.getBackend());
if (level <= 0) return;
event.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() || block.getType() != Material.MAGMA_BLOCK) return true;
Pair<Long, Integer> pair = BLOCKS_TO_DESTROY.get(block);
long time = pair.getFirst();
if (now >= time) {
if (Version.isAtLeast(Version.V1_19_R3)) {
block.getWorld().getPlayers().forEach(player -> {
player.sendBlockDamage(block.getLocation(), 0F, pair.getSecond());
});
}
block.setType(Material.LAVA);
UniParticle.blockCrack(Material.MAGMA_BLOCK).play(block.getLocation(), 0.5, 0.7, 0.5, 0.03, 30);
return true;
}
else if (Version.isAtLeast(Version.V1_19_R3)) {
long diff = TimeUnit.MILLISECONDS.toSeconds(time - now);
float progress = (float) (1D - Math.min(1D, diff / 5D));
if (progress > 1F) progress = 1F;
if (progress < 0F) progress = 0F;
float finalProgress = progress;
block.getWorld().getPlayers().forEach(player -> {
player.sendBlockDamage(block.getLocation(), finalProgress, pair.getSecond());
});
}
return false;
});
}
}
}

View File

@ -0,0 +1,79 @@
package su.nightexpress.excellentenchants.enchantment.impl.armor;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.LivingEntity;
import org.bukkit.event.EventPriority;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.potion.PotionEffectType;
import org.jetbrains.annotations.NotNull;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.api.enchantment.meta.Chanced;
import su.nightexpress.excellentenchants.api.enchantment.meta.Potioned;
import su.nightexpress.excellentenchants.api.enchantment.type.CombatEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.meta.ChanceImplementation;
import su.nightexpress.excellentenchants.enchantment.impl.meta.PotionImplementation;
public class HardenedEnchant extends ExcellentEnchant implements Chanced, Potioned, CombatEnchant {
public static final String ID = "hardened";
private ChanceImplementation chanceImplementation;
private PotionImplementation potionImplementation;
public HardenedEnchant(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription(Placeholders.ENCHANTMENT_CHANCE + "% chance to obtain " + Placeholders.ENCHANTMENT_POTION_TYPE + " " + Placeholders.ENCHANTMENT_POTION_LEVEL + " (" + Placeholders.ENCHANTMENT_POTION_DURATION + "s.) when damaged.");
this.getDefaults().setLevelMax(3);
this.getDefaults().setTier(0.4);
}
@Override
public void loadSettings() {
super.loadSettings();
this.chanceImplementation = ChanceImplementation.create(this,
"30.0 * " + Placeholders.ENCHANTMENT_LEVEL);
this.potionImplementation = PotionImplementation.create(this, PotionEffectType.DAMAGE_RESISTANCE, false,
"3.0" + Placeholders.ENCHANTMENT_LEVEL,
Placeholders.ENCHANTMENT_LEVEL);
}
@NotNull
@Override
public ChanceImplementation getChanceImplementation() {
return chanceImplementation;
}
@NotNull
@Override
public PotionImplementation getPotionImplementation() {
return potionImplementation;
}
@Override
@NotNull
public EnchantmentTarget getCategory() {
return EnchantmentTarget.ARMOR_TORSO;
}
@NotNull
@Override
public EventPriority getProtectPriority() {
return EventPriority.HIGHEST;
}
@Override
public boolean onAttack(@NotNull EntityDamageByEntityEvent event, @NotNull LivingEntity damager, @NotNull LivingEntity victim, @NotNull ItemStack weapon, int level) {
return false;
}
@Override
public boolean onProtect(@NotNull EntityDamageByEntityEvent event, @NotNull LivingEntity damager, @NotNull LivingEntity victim, @NotNull ItemStack weapon, int level) {
if (!this.checkTriggerChance(level)) return false;
return this.addEffect(victim, level);
}
}

View File

@ -0,0 +1,85 @@
package su.nightexpress.excellentenchants.enchantment.impl.armor;
import org.bukkit.Material;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.LivingEntity;
import org.bukkit.event.EventPriority;
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.utils.values.UniParticle;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.api.enchantment.meta.Chanced;
import su.nightexpress.excellentenchants.api.enchantment.meta.Potioned;
import su.nightexpress.excellentenchants.api.enchantment.type.CombatEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.meta.ChanceImplementation;
import su.nightexpress.excellentenchants.enchantment.impl.meta.PotionImplementation;
public class IceShieldEnchant extends ExcellentEnchant implements Chanced, Potioned, CombatEnchant {
public static final String ID = "ice_shield";
private ChanceImplementation chanceImplementation;
private PotionImplementation potionImplementation;
public IceShieldEnchant(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription(Placeholders.ENCHANTMENT_CHANCE + "% chance to freeze and apply " + Placeholders.ENCHANTMENT_POTION_TYPE + " " + Placeholders.ENCHANTMENT_POTION_LEVEL + " (" + Placeholders.ENCHANTMENT_POTION_DURATION + "s.) on attacker.");
this.getDefaults().setLevelMax(3);
this.getDefaults().setTier(0.1);
}
@Override
public void loadSettings() {
super.loadSettings();
this.chanceImplementation = ChanceImplementation.create(this, "25.0 * " + Placeholders.ENCHANTMENT_LEVEL);
this.potionImplementation = PotionImplementation.create(this, PotionEffectType.SLOW, false,
"3.0 + " + Placeholders.ENCHANTMENT_LEVEL,
Placeholders.ENCHANTMENT_LEVEL);
}
@NotNull
@Override
public EnchantmentTarget getCategory() {
return EnchantmentTarget.ARMOR_TORSO;
}
@NotNull
@Override
public EventPriority getProtectPriority() {
return EventPriority.HIGHEST;
}
@Override
@NotNull
public Chanced getChanceImplementation() {
return this.chanceImplementation;
}
@NotNull
@Override
public PotionImplementation getPotionImplementation() {
return potionImplementation;
}
@Override
public boolean onAttack(@NotNull EntityDamageByEntityEvent event, @NotNull LivingEntity damager, @NotNull LivingEntity victim, @NotNull ItemStack weapon, int level) {
return false;
}
@Override
public boolean onProtect(@NotNull EntityDamageByEntityEvent event, @NotNull LivingEntity damager, @NotNull LivingEntity victim, @NotNull ItemStack weapon, int level) {
if (!this.checkTriggerChance(level)) return false;
if (!this.addEffect(damager, level)) return false;
damager.setFreezeTicks(damager.getMaxFreezeTicks());
if (this.hasVisualEffects()) {
UniParticle.blockCrack(Material.ICE).play(damager.getEyeLocation(), 0.25, 0.1, 20);
}
return true;
}
}

View File

@ -0,0 +1,59 @@
package su.nightexpress.excellentenchants.enchantment.impl.armor;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.LivingEntity;
import org.bukkit.inventory.ItemStack;
import org.bukkit.potion.PotionEffectType;
import org.jetbrains.annotations.NotNull;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.api.enchantment.meta.Potioned;
import su.nightexpress.excellentenchants.api.enchantment.type.PassiveEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.meta.PeriodImplementation;
import su.nightexpress.excellentenchants.enchantment.impl.meta.PotionImplementation;
public class JumpingEnchant extends ExcellentEnchant implements Potioned, PassiveEnchant {
public static final String ID = "bunny_hop";
private PotionImplementation potionImplementation;
private PeriodImplementation periodImplementation;
public JumpingEnchant(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setLevelMax(3);
this.getDefaults().setTier(0.1);
this.getDefaults().setDescription("Grants permanent " + Placeholders.ENCHANTMENT_POTION_TYPE + " " + Placeholders.ENCHANTMENT_POTION_LEVEL + " effect.");
}
@Override
public void loadSettings() {
super.loadSettings();
this.potionImplementation = PotionImplementation.create(this, PotionEffectType.JUMP, true);
this.periodImplementation = PeriodImplementation.create(this, "100");
}
@NotNull
@Override
public PotionImplementation getPotionImplementation() {
return potionImplementation;
}
@NotNull
@Override
public PeriodImplementation getPeriodImplementation() {
return periodImplementation;
}
@Override
@NotNull
public EnchantmentTarget getCategory() {
return EnchantmentTarget.ARMOR_FEET;
}
@Override
public boolean onTrigger(@NotNull LivingEntity entity, @NotNull ItemStack item, int level) {
return this.addEffect(entity, level);
}
}

View File

@ -0,0 +1,114 @@
package su.nightexpress.excellentenchants.enchantment.impl.armor;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Item;
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.EntityDamageByEntityEvent;
import org.bukkit.event.entity.EntityDeathEvent;
import org.bukkit.event.entity.EntityResurrectEvent;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import su.nexmedia.engine.api.config.JOption;
import su.nexmedia.engine.api.manager.EventListener;
import su.nexmedia.engine.utils.NumberUtil;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.api.enchantment.meta.Chanced;
import su.nightexpress.excellentenchants.api.enchantment.type.DeathEnchant;
import su.nightexpress.excellentenchants.enchantment.config.EnchantScaler;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.meta.ChanceImplementation;
public class KamikadzeEnchant extends ExcellentEnchant implements Chanced, DeathEnchant, EventListener {
public static final String ID = "self_destruction";
private static final String PLACEHOLDER_EXPLOSION_POWER = "%enchantment_explosion_power%";
private EnchantScaler explosionSize;
private boolean applyOnResurrect;
private ChanceImplementation chanceImplementation;
private Entity exploder;
public KamikadzeEnchant(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription("%enchantment_trigger_chance%% chance to create an explosion on death.");
this.getDefaults().setLevelMax(3);
this.getDefaults().setTier(0.3);
}
@Override
public void loadSettings() {
super.loadSettings();
this.chanceImplementation = ChanceImplementation.create(this,
"20.0 + " + Placeholders.ENCHANTMENT_LEVEL + " * 10");
this.applyOnResurrect = JOption.create("Settings.Apply_On_Resurrect", true,
"Sets whether or not enchantment will trigger on resurrect (when a totem is used)."
).read(cfg);
this.explosionSize = EnchantScaler.read(this, "Settings.Explosion.Size",
"1.0" + Placeholders.ENCHANTMENT_LEVEL,
"A size of the explosion. The more size - the bigger the damage.");
this.addPlaceholder(PLACEHOLDER_EXPLOSION_POWER, level -> NumberUtil.format(this.getExplosionSize(level)));
}
@NotNull
@Override
public ChanceImplementation getChanceImplementation() {
return chanceImplementation;
}
@Override
@NotNull
public EnchantmentTarget getCategory() {
return EnchantmentTarget.ARMOR_TORSO;
}
public boolean isApplyOnResurrect() {
return this.applyOnResurrect;
}
public final double getExplosionSize(int level) {
return this.explosionSize.getValue(level);
}
public boolean createExplosion(@NotNull LivingEntity entity, @NotNull ItemStack item, int level) {
if (!this.checkTriggerChance(level)) return false;
float size = (float) this.getExplosionSize(level);
this.exploder = entity;
boolean exploded = entity.getWorld().createExplosion(entity.getLocation(), size, false, false, entity);
this.exploder = null;
return exploded;
}
@Override
public boolean onDeath(@NotNull EntityDeathEvent event, @NotNull LivingEntity entity, ItemStack item, int level) {
return this.createExplosion(entity, item, level);
}
@Override
public boolean onResurrect(@NotNull EntityResurrectEvent event, @NotNull LivingEntity entity, @NotNull ItemStack item, int level) {
return this.isApplyOnResurrect() && this.createExplosion(entity, item, level);
}
@Override
public boolean onKill(@NotNull EntityDeathEvent event, @NotNull LivingEntity entity, @NotNull Player killer, ItemStack weapon, int level) {
return false;
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onItemDamage(EntityDamageByEntityEvent event) {
if (this.exploder == null || event.getDamager() != this.exploder) return;
if (event.getEntity() instanceof Item || event.getEntity() == this.exploder) {
event.setCancelled(true);
}
}
}

View File

@ -0,0 +1,59 @@
package su.nightexpress.excellentenchants.enchantment.impl.armor;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.LivingEntity;
import org.bukkit.inventory.ItemStack;
import org.bukkit.potion.PotionEffectType;
import org.jetbrains.annotations.NotNull;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.api.enchantment.meta.Potioned;
import su.nightexpress.excellentenchants.api.enchantment.type.PassiveEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.meta.PeriodImplementation;
import su.nightexpress.excellentenchants.enchantment.impl.meta.PotionImplementation;
public class NightVisionEnchant extends ExcellentEnchant implements Potioned, PassiveEnchant {
public static final String ID = "night_vision";
private PotionImplementation potionImplementation;
private PeriodImplementation periodImplementation;
public NightVisionEnchant(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription("Grants permanent " + Placeholders.ENCHANTMENT_POTION_TYPE + " " + Placeholders.ENCHANTMENT_POTION_LEVEL + " effect.");
this.getDefaults().setLevelMax(1);
this.getDefaults().setTier(0.7);
}
@Override
public void loadSettings() {
super.loadSettings();
this.potionImplementation = PotionImplementation.create(this, PotionEffectType.NIGHT_VISION, true);
this.periodImplementation = PeriodImplementation.create(this, "100");
}
@NotNull
@Override
public PotionImplementation getPotionImplementation() {
return potionImplementation;
}
@NotNull
@Override
public PeriodImplementation getPeriodImplementation() {
return periodImplementation;
}
@Override
@NotNull
public EnchantmentTarget getCategory() {
return EnchantmentTarget.ARMOR_HEAD;
}
@Override
public boolean onTrigger(@NotNull LivingEntity entity, @NotNull ItemStack item, int level) {
return this.addEffect(entity, level);
}
}

View File

@ -0,0 +1,110 @@
package su.nightexpress.excellentenchants.enchantment.impl.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.jetbrains.annotations.NotNull;
import su.nexmedia.engine.utils.EntityUtil;
import su.nexmedia.engine.utils.NumberUtil;
import su.nexmedia.engine.utils.values.UniParticle;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.api.enchantment.meta.Chanced;
import su.nightexpress.excellentenchants.api.enchantment.type.PassiveEnchant;
import su.nightexpress.excellentenchants.enchantment.config.EnchantScaler;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.meta.ChanceImplementation;
import su.nightexpress.excellentenchants.enchantment.impl.meta.PeriodImplementation;
public class RegrowthEnchant extends ExcellentEnchant implements Chanced, PassiveEnchant {
public static final String ID = "regrowth";
private static final String PLACEHOLDER_HEAL_AMOUNT = "%enchantment_heal_amount%";
private static final String PLACEHOLDER_HEAL_MIN_HEALTH = "%enchantment_heal_min_health%";
private static final String PLACEHOLDER_HEAL_MAX_HEALTH = "%enchantment_heal_max_health%";
private EnchantScaler healMinHealth;
private EnchantScaler healMaxHealth;
private EnchantScaler healAmount;
private ChanceImplementation chanceImplementation;
private PeriodImplementation periodImplementation;
public RegrowthEnchant(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription("Restores " + PLACEHOLDER_HEAL_AMOUNT + " hearts every few seconds.");
this.getDefaults().setLevelMax(5);
this.getDefaults().setTier(0.7);
}
@Override
public void loadSettings() {
super.loadSettings();
this.chanceImplementation = ChanceImplementation.create(this,
"20.0 + " + Placeholders.ENCHANTMENT_LEVEL + " * 5");
this.periodImplementation = PeriodImplementation.create(this, "100");
this.healMinHealth = EnchantScaler.read(this, "Settings.Heal.Min_Health", "0.5",
"Minimal entity health for the enchantment to have effect.");
this.healMaxHealth = EnchantScaler.read(this, "Settings.Heal.Max_Health", "20.0",
"Maximal entity health when the enchantment will not heal anymore.");
this.healAmount = EnchantScaler.read(this, "Settings.Heal.Amount", "0.25",
"Amount of hearts to be restored.");
this.addPlaceholder(PLACEHOLDER_HEAL_AMOUNT, level -> NumberUtil.format(this.getHealAmount(level)));
this.addPlaceholder(PLACEHOLDER_HEAL_MIN_HEALTH, level -> NumberUtil.format(this.getHealMaxHealth(level)));
this.addPlaceholder(PLACEHOLDER_HEAL_MAX_HEALTH, level -> NumberUtil.format(this.getHealMaxHealth(level)));
}
@NotNull
@Override
public ChanceImplementation getChanceImplementation() {
return chanceImplementation;
}
@NotNull
@Override
public PeriodImplementation getPeriodImplementation() {
return periodImplementation;
}
@NotNull
@Override
public EnchantmentTarget getCategory() {
return EnchantmentTarget.ARMOR_TORSO;
}
public double getHealAmount(int level) {
return this.healAmount.getValue(level);
}
public double getHealMinHealth(int level) {
return this.healMinHealth.getValue(level);
}
public double getHealMaxHealth(int level) {
return this.healMaxHealth.getValue(level);
}
@Override
public boolean onTrigger(@NotNull LivingEntity entity, @NotNull ItemStack item, int level) {
if (!this.checkTriggerChance(level)) return false;
double healthMax = EntityUtil.getAttribute(entity, Attribute.GENERIC_MAX_HEALTH);
double healthHas = entity.getHealth();
if (healthHas < this.getHealMinHealth(level) || healthHas > this.getHealMaxHealth(level)) return false;
if (healthHas >= healthMax) return false;
double amount = Math.min(healthMax, healthHas + this.getHealAmount(level));
entity.setHealth(amount);
if (this.hasVisualEffects()) {
UniParticle.of(Particle.HEART).play(entity.getEyeLocation(), 0.25, 0.1, 5);
}
return true;
}
}

View File

@ -0,0 +1,78 @@
package su.nightexpress.excellentenchants.enchantment.impl.armor;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import su.nexmedia.engine.utils.NumberUtil;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.api.enchantment.type.PassiveEnchant;
import su.nightexpress.excellentenchants.enchantment.config.EnchantScaler;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.meta.PeriodImplementation;
public class SaturationEnchant extends ExcellentEnchant implements PassiveEnchant {
public static final String ID = "saturation";
private static final String PLACEHOLDER_SATURATION_AMOUNT = "%enchantment_saturation_amount%";
private static final String PLACEHOLDER_SATURATION_MAX_FOOD_LEVEL = "%enchantment_saturation_max_food_level%";
private EnchantScaler saturationAmount;
private EnchantScaler saturationMaxFoodLevel;
private PeriodImplementation periodImplementation;
public SaturationEnchant(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription("Restores " + PLACEHOLDER_SATURATION_AMOUNT + " food points every few seconds.");
this.getDefaults().setLevelMax(3);
this.getDefaults().setTier(0.5);
}
@Override
public void loadSettings() {
super.loadSettings();
this.periodImplementation = PeriodImplementation.create(this, "100");
this.saturationAmount = EnchantScaler.read(this, "Settings.Saturation.Amount", Placeholders.ENCHANTMENT_LEVEL,
"Amount of food points to restore.");
this.saturationMaxFoodLevel = EnchantScaler.read(this, "Settings.Saturation.Max_Food_Level", "20",
"Maximal player's food level for the enchantment to stop feeding them.");
this.addPlaceholder(PLACEHOLDER_SATURATION_AMOUNT, level -> NumberUtil.format(this.getSaturationAmount(level)));
this.addPlaceholder(PLACEHOLDER_SATURATION_MAX_FOOD_LEVEL, level -> NumberUtil.format(this.getMaxFoodLevel(level)));
}
@NotNull
@Override
public PeriodImplementation getPeriodImplementation() {
return periodImplementation;
}
@Override
@NotNull
public EnchantmentTarget getCategory() {
return EnchantmentTarget.ARMOR_HEAD;
}
public final int getSaturationAmount(int level) {
return (int) this.saturationAmount.getValue(level);
}
public final int getMaxFoodLevel(int level) {
return (int) this.saturationMaxFoodLevel.getValue(level);
}
@Override
public boolean onTrigger(@NotNull LivingEntity entity, @NotNull ItemStack item, int level) {
if (!(entity instanceof Player player)) return false;
if (player.getFoodLevel() >= this.getMaxFoodLevel(level)) return false;
int amount = this.getSaturationAmount(level);
player.setFoodLevel(Math.min(20, player.getFoodLevel() + amount));
return true;
}
}

View File

@ -0,0 +1,59 @@
package su.nightexpress.excellentenchants.enchantment.impl.armor;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.LivingEntity;
import org.bukkit.inventory.ItemStack;
import org.bukkit.potion.PotionEffectType;
import org.jetbrains.annotations.NotNull;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.api.enchantment.meta.Potioned;
import su.nightexpress.excellentenchants.api.enchantment.type.PassiveEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.meta.PeriodImplementation;
import su.nightexpress.excellentenchants.enchantment.impl.meta.PotionImplementation;
public class SpeedyEnchant extends ExcellentEnchant implements Potioned, PassiveEnchant {
public static final String ID = "sonic";
private PotionImplementation potionImplementation;
private PeriodImplementation periodImplementation;
public SpeedyEnchant(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription("Grants permanent " + Placeholders.ENCHANTMENT_POTION_TYPE + " " + Placeholders.ENCHANTMENT_POTION_LEVEL + " effect.");
this.getDefaults().setLevelMax(3);
this.getDefaults().setTier(0.3);
}
@Override
public void loadSettings() {
super.loadSettings();
this.potionImplementation = PotionImplementation.create(this, PotionEffectType.SPEED, true);
this.periodImplementation = PeriodImplementation.create(this, "100");
}
@NotNull
@Override
public PotionImplementation getPotionImplementation() {
return potionImplementation;
}
@NotNull
@Override
public PeriodImplementation getPeriodImplementation() {
return periodImplementation;
}
@Override
@NotNull
public EnchantmentTarget getCategory() {
return EnchantmentTarget.ARMOR_FEET;
}
@Override
public boolean onTrigger(@NotNull LivingEntity entity, @NotNull ItemStack item, int level) {
return this.addEffect(entity, level);
}
}

View File

@ -0,0 +1,81 @@
package su.nightexpress.excellentenchants.enchantment.impl.armor;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.LivingEntity;
import org.bukkit.event.EventPriority;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import su.nexmedia.engine.utils.NumberUtil;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.api.enchantment.meta.Chanced;
import su.nightexpress.excellentenchants.api.enchantment.type.CombatEnchant;
import su.nightexpress.excellentenchants.enchantment.config.EnchantScaler;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.meta.ChanceImplementation;
public class StoppingForceEnchant extends ExcellentEnchant implements Chanced, CombatEnchant {
public static final String ID = "stopping_force";
public static final String PLACEHOLDER_KNOCKBACK_RESISTANCE = "%knockback_resistance%";
private ChanceImplementation chanceImplementation;
private EnchantScaler knockbackModifier;
public StoppingForceEnchant(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription(Placeholders.ENCHANTMENT_CHANCE + "% chance to resist knockback in combat by " + PLACEHOLDER_KNOCKBACK_RESISTANCE + "%.");
this.getDefaults().setLevelMax(3);
this.getDefaults().setTier(0.5);
}
@Override
public void loadSettings() {
super.loadSettings();
this.chanceImplementation = ChanceImplementation.create(this, "100.0");
this.knockbackModifier = EnchantScaler.read(this, "Settings.Knockback_Modifier",
"0.7 - " + Placeholders.ENCHANTMENT_LEVEL + " / 5.0",
"Sets the knockback multiplier when taking damage.", "Lower value = less knockback.");
this.addPlaceholder(PLACEHOLDER_KNOCKBACK_RESISTANCE, level -> NumberUtil.format(this.getKnockbackModifier(level) * 100));
}
public double getKnockbackModifier(int level) {
return this.knockbackModifier.getValue(level);
}
@NotNull
@Override
public EnchantmentTarget getCategory() {
return EnchantmentTarget.ARMOR_LEGS;
}
@NotNull
@Override
public EventPriority getProtectPriority() {
return EventPriority.HIGHEST;
}
@NotNull
@Override
public Chanced getChanceImplementation() {
return this.chanceImplementation;
}
@Override
public boolean onAttack(@NotNull EntityDamageByEntityEvent event, @NotNull LivingEntity damager, @NotNull LivingEntity victim, @NotNull ItemStack weapon, int level) {
return false;
}
@Override
public boolean onProtect(@NotNull EntityDamageByEntityEvent event, @NotNull LivingEntity damager, @NotNull LivingEntity victim, @NotNull ItemStack weapon, int level) {
if (!this.checkTriggerChance(level)) return false;
this.plugin.runTask(task -> {
victim.setVelocity(victim.getVelocity().multiply(this.getKnockbackModifier(level)));
});
return true;
}
}

View File

@ -0,0 +1,96 @@
package su.nightexpress.excellentenchants.enchantment.impl.bow;
import org.bukkit.Particle;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.AbstractArrow;
import org.bukkit.entity.Arrow;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Projectile;
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.potion.PotionEffectType;
import org.jetbrains.annotations.NotNull;
import su.nexmedia.engine.utils.values.UniParticle;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.api.enchantment.meta.Arrowed;
import su.nightexpress.excellentenchants.api.enchantment.meta.Chanced;
import su.nightexpress.excellentenchants.api.enchantment.meta.Potioned;
import su.nightexpress.excellentenchants.api.enchantment.type.BowEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.meta.ArrowImplementation;
import su.nightexpress.excellentenchants.enchantment.impl.meta.ChanceImplementation;
import su.nightexpress.excellentenchants.enchantment.impl.meta.PotionImplementation;
public class DarknessArrowsEnchant extends ExcellentEnchant implements Chanced, Arrowed, Potioned, BowEnchant {
public static final String ID = "darkness_arrows";
private ArrowImplementation arrowImplementation;
private ChanceImplementation chanceImplementation;
private PotionImplementation potionImplementation;
public DarknessArrowsEnchant(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription(Placeholders.ENCHANTMENT_CHANCE + "% chance to launch an arrow with " + Placeholders.ENCHANTMENT_POTION_TYPE + " " + Placeholders.ENCHANTMENT_POTION_LEVEL + " (" + Placeholders.ENCHANTMENT_POTION_DURATION + "s.)");
this.getDefaults().setLevelMax(3);
this.getDefaults().setTier(0.1);
this.getDefaults().setConflicts(EnchantEnderBow.ID, EnchantGhast.ID, EnchantBomber.ID);
}
@Override
public void loadSettings() {
super.loadSettings();
this.arrowImplementation = ArrowImplementation.create(this, UniParticle.of(Particle.ASH));
this.chanceImplementation = ChanceImplementation.create(this,
"25.0 + " + Placeholders.ENCHANTMENT_LEVEL + " * 5.0");
this.potionImplementation = PotionImplementation.create(this, PotionEffectType.DARKNESS, false,
"4.0 + " + Placeholders.ENCHANTMENT_LEVEL,
Placeholders.ENCHANTMENT_LEVEL);
}
@NotNull
@Override
public ArrowImplementation getArrowImplementation() {
return arrowImplementation;
}
@NotNull
@Override
public ChanceImplementation getChanceImplementation() {
return chanceImplementation;
}
@NotNull
@Override
public PotionImplementation getPotionImplementation() {
return potionImplementation;
}
@NotNull
@Override
public EnchantmentTarget getCategory() {
return EnchantmentTarget.BOW;
}
@Override
public boolean onShoot(@NotNull EntityShootBowEvent event, @NotNull LivingEntity shooter, @NotNull ItemStack bow, int level) {
if (!(event.getProjectile() instanceof Arrow arrow)) return false;
if (!this.checkTriggerChance(level)) return false;
arrow.setPickupStatus(AbstractArrow.PickupStatus.DISALLOWED);
return arrow.addCustomEffect(this.createEffect(level), true);
}
@Override
public boolean onHit(@NotNull ProjectileHitEvent event, LivingEntity user, @NotNull Projectile projectile, @NotNull ItemStack bow, int level) {
return false;
}
@Override
public boolean onDamage(@NotNull EntityDamageByEntityEvent event, @NotNull Projectile projectile, @NotNull LivingEntity shooter, @NotNull LivingEntity victim, @NotNull ItemStack weapon, int level) {
return false;
}
}

View File

@ -0,0 +1,104 @@
package su.nightexpress.excellentenchants.enchantment.impl.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.EventPriority;
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.utils.NumberUtil;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.api.enchantment.meta.Chanced;
import su.nightexpress.excellentenchants.api.enchantment.type.BowEnchant;
import su.nightexpress.excellentenchants.enchantment.config.EnchantScaler;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.meta.ChanceImplementation;
public class EnchantBomber extends ExcellentEnchant implements Chanced, BowEnchant {
public static final String ID = "bomber";
public static final String PLACEHOLDER_FUSE_TICKS = "%enchantment_fuse_ticks%";
private EnchantScaler fuseTicks;
private ChanceImplementation chanceImplementation;
public EnchantBomber(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription(Placeholders.ENCHANTMENT_CHANCE + "% chance to launch TNT that explodes in " + PLACEHOLDER_FUSE_TICKS + "s.");
this.getDefaults().setLevelMax(3);
this.getDefaults().setTier(0.7);
this.getDefaults().setConflicts(
EnchantEnderBow.ID, EnchantGhast.ID,
EnchantExplosiveArrows.ID, EnchantPoisonedArrows.ID, EnchantConfusingArrows.ID,
EnchantWitheredArrows.ID, EnchantElectrifiedArrows.ID, EnchantDragonfireArrows.ID,
DarknessArrowsEnchant.ID,
EnchantHover.ID, FlareEnchant.ID,
Enchantment.ARROW_FIRE.getKey().getKey(),
Enchantment.ARROW_KNOCKBACK.getKey().getKey(),
Enchantment.ARROW_DAMAGE.getKey().getKey()
);
}
@Override
public void loadSettings() {
super.loadSettings();
this.chanceImplementation = ChanceImplementation.create(this,
"5.0 * " + Placeholders.ENCHANTMENT_LEVEL);
this.fuseTicks = EnchantScaler.read(this, "Settings.Fuse_Ticks",
"100 - " + Placeholders.ENCHANTMENT_LEVEL + " * 10",
"Sets fuse ticks (before it will explode) for the launched TNT.");
this.addPlaceholder(PLACEHOLDER_FUSE_TICKS, level -> NumberUtil.format((double) this.getFuseTicks(level) / 20D));
}
@NotNull
@Override
public ChanceImplementation getChanceImplementation() {
return chanceImplementation;
}
public int getFuseTicks(int level) {
return (int) this.fuseTicks.getValue(level);
}
@Override
@NotNull
public EnchantmentTarget getCategory() {
return EnchantmentTarget.BOW;
}
@NotNull
@Override
public EventPriority getShootPriority() {
return EventPriority.LOWEST;
}
@Override
public boolean onShoot(@NotNull EntityShootBowEvent event, @NotNull LivingEntity shooter, @NotNull ItemStack bow, int level) {
if (!this.checkTriggerChance(level)) return false;
if (!(event.getProjectile() instanceof Projectile projectile)) return false;
TNTPrimed primed = projectile.getWorld().spawn(projectile.getLocation(), TNTPrimed.class);
primed.setVelocity(projectile.getVelocity().multiply(event.getForce() * 1.25));
primed.setFuseTicks(this.getFuseTicks(level));
primed.setSource(shooter);
event.setProjectile(primed);
return true;
}
@Override
public boolean onHit(@NotNull ProjectileHitEvent event, LivingEntity user, @NotNull Projectile projectile, @NotNull ItemStack bow, int level) {
return false;
}
@Override
public boolean onDamage(@NotNull EntityDamageByEntityEvent event, @NotNull Projectile projectile, @NotNull LivingEntity shooter, @NotNull LivingEntity victim, @NotNull ItemStack weapon, int level) {
return false;
}
}

View File

@ -0,0 +1,96 @@
package su.nightexpress.excellentenchants.enchantment.impl.bow;
import org.bukkit.Particle;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.AbstractArrow;
import org.bukkit.entity.Arrow;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Projectile;
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.potion.PotionEffectType;
import org.jetbrains.annotations.NotNull;
import su.nexmedia.engine.utils.values.UniParticle;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.api.enchantment.meta.Arrowed;
import su.nightexpress.excellentenchants.api.enchantment.meta.Chanced;
import su.nightexpress.excellentenchants.api.enchantment.meta.Potioned;
import su.nightexpress.excellentenchants.api.enchantment.type.BowEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.meta.ArrowImplementation;
import su.nightexpress.excellentenchants.enchantment.impl.meta.ChanceImplementation;
import su.nightexpress.excellentenchants.enchantment.impl.meta.PotionImplementation;
public class EnchantConfusingArrows extends ExcellentEnchant implements Chanced, Arrowed, Potioned, BowEnchant {
public static final String ID = "confusing_arrows";
private ArrowImplementation arrowImplementation;
private ChanceImplementation chanceImplementation;
private PotionImplementation potionImplementation;
public EnchantConfusingArrows(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription(Placeholders.ENCHANTMENT_CHANCE + "% chance to launch an arrow with " + Placeholders.ENCHANTMENT_POTION_TYPE + " " + Placeholders.ENCHANTMENT_POTION_LEVEL + " (" + Placeholders.ENCHANTMENT_POTION_DURATION + "s.)");
this.getDefaults().setLevelMax(3);
this.getDefaults().setTier(0.1);
this.getDefaults().setConflicts(EnchantEnderBow.ID, EnchantGhast.ID, EnchantBomber.ID);
}
@Override
public void loadSettings() {
super.loadSettings();
this.arrowImplementation = ArrowImplementation.create(this, UniParticle.of(Particle.SPELL_MOB));
this.chanceImplementation = ChanceImplementation.create(this,
"20.0 + " + Placeholders.ENCHANTMENT_LEVEL + " * 5.0");
this.potionImplementation = PotionImplementation.create(this, PotionEffectType.CONFUSION, false,
"6.0 + " + Placeholders.ENCHANTMENT_LEVEL + " * 3.0",
Placeholders.ENCHANTMENT_LEVEL);
}
@NotNull
@Override
public ArrowImplementation getArrowImplementation() {
return arrowImplementation;
}
@NotNull
@Override
public ChanceImplementation getChanceImplementation() {
return chanceImplementation;
}
@NotNull
@Override
public PotionImplementation getPotionImplementation() {
return potionImplementation;
}
@NotNull
@Override
public EnchantmentTarget getCategory() {
return EnchantmentTarget.BOW;
}
@Override
public boolean onShoot(@NotNull EntityShootBowEvent event, @NotNull LivingEntity shooter, @NotNull ItemStack bow, int level) {
if (!(event.getProjectile() instanceof Arrow arrow)) return false;
if (!this.checkTriggerChance(level)) return false;
arrow.setPickupStatus(AbstractArrow.PickupStatus.DISALLOWED);
return arrow.addCustomEffect(this.createEffect(level), true);
}
@Override
public boolean onHit(@NotNull ProjectileHitEvent event, LivingEntity user, @NotNull Projectile projectile, @NotNull ItemStack bow, int level) {
return false;
}
@Override
public boolean onDamage(@NotNull EntityDamageByEntityEvent event, @NotNull Projectile projectile, @NotNull LivingEntity shooter, @NotNull LivingEntity victim, @NotNull ItemStack weapon, int level) {
return false;
}
}

View File

@ -0,0 +1,155 @@
package su.nightexpress.excellentenchants.enchantment.impl.bow;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Particle;
import org.bukkit.World;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.AreaEffectCloud;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Projectile;
import org.bukkit.entity.ThrownPotion;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.entity.EntityShootBowEvent;
import org.bukkit.event.entity.LingeringPotionSplashEvent;
import org.bukkit.event.entity.ProjectileHitEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.PotionMeta;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.projectiles.ProjectileSource;
import org.jetbrains.annotations.NotNull;
import su.nexmedia.engine.utils.ItemUtil;
import su.nexmedia.engine.utils.NumberUtil;
import su.nexmedia.engine.utils.values.UniParticle;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.api.enchantment.meta.Arrowed;
import su.nightexpress.excellentenchants.api.enchantment.meta.Chanced;
import su.nightexpress.excellentenchants.api.enchantment.type.BowEnchant;
import su.nightexpress.excellentenchants.enchantment.config.EnchantScaler;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.meta.ArrowImplementation;
import su.nightexpress.excellentenchants.enchantment.impl.meta.ChanceImplementation;
public class EnchantDragonfireArrows extends ExcellentEnchant implements Chanced, Arrowed, BowEnchant {
public static final String ID = "dragonfire_arrows";
public static final String PLACEHOLDER_FIRE_RADIUS = "%enchantment_fire_radius%";
public static final String PLACEHOLDER_FIRE_DURATION = "%enchantment_fire_duration%";
private EnchantScaler fireDuration;
private EnchantScaler fireRadius;
private ArrowImplementation arrowImplementation;
private ChanceImplementation chanceImplementation;
public EnchantDragonfireArrows(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription(Placeholders.ENCHANTMENT_CHANCE + "% chance to launch an dragonfire arrow (R=" + PLACEHOLDER_FIRE_RADIUS + ", " + PLACEHOLDER_FIRE_DURATION + "s).");
this.getDefaults().setLevelMax(3);
this.getDefaults().setTier(0.7);
this.getDefaults().setConflicts(EnchantEnderBow.ID, EnchantGhast.ID, EnchantBomber.ID);
}
@Override
public void loadSettings() {
super.loadSettings();
this.arrowImplementation = ArrowImplementation.create(this, UniParticle.of(Particle.DRAGON_BREATH));
this.chanceImplementation = ChanceImplementation.create(this,
"10.0 + " + Placeholders.ENCHANTMENT_LEVEL + " * 5");
this.fireDuration = EnchantScaler.read(this, "Settings.Fire.Duration",
"100 * " + Placeholders.ENCHANTMENT_LEVEL,
"Sets the dragonfire cloud effect duration (in ticks). 20 ticks = 1 second.");
this.fireRadius = EnchantScaler.read(this, "Settings.Fire.Radius",
"2.0 + " + Placeholders.ENCHANTMENT_LEVEL,
"Sets the dragonfire cloud effect radius.");
this.addPlaceholder(PLACEHOLDER_FIRE_DURATION, level -> NumberUtil.format(this.getFireDuration(level) / 20D));
this.addPlaceholder(PLACEHOLDER_FIRE_RADIUS, level -> NumberUtil.format(this.getFireRadius(level)));
}
@NotNull
@Override
public ArrowImplementation getArrowImplementation() {
return arrowImplementation;
}
@NotNull
@Override
public ChanceImplementation getChanceImplementation() {
return chanceImplementation;
}
@NotNull
@Override
public EnchantmentTarget getCategory() {
return EnchantmentTarget.BOW;
}
public int getFireDuration(int level) {
return (int) this.fireDuration.getValue(level);
}
public double getFireRadius(int level) {
return this.fireRadius.getValue(level);
}
@Override
public boolean onShoot(@NotNull EntityShootBowEvent event, @NotNull LivingEntity shooter, @NotNull ItemStack bow, int level) {
return this.checkTriggerChance(level);
}
@Override
public boolean onHit(@NotNull ProjectileHitEvent event, LivingEntity user, @NotNull Projectile projectile, @NotNull ItemStack bow, int level) {
if (!this.isOurProjectile(projectile)) return false;
if (event.getHitEntity() != null) return false;
if (projectile.getShooter() == null) return false;
this.createCloud(projectile.getShooter(), projectile.getLocation() , level);
return false;
}
@Override
public boolean onDamage(@NotNull EntityDamageByEntityEvent event, @NotNull Projectile projectile, @NotNull LivingEntity shooter, @NotNull LivingEntity victim, @NotNull ItemStack weapon, int level) {
if (!this.isOurProjectile(projectile)) return false;
this.createCloud(shooter, victim.getLocation(), level);
return false;
}
private void createCloud(@NotNull ProjectileSource shooter, @NotNull Location location, int level) {
World world = location.getWorld();
if (world == null) return;
// There are some tweaks to respect protection plugins using even call.
ItemStack item = new ItemStack(Material.LINGERING_POTION);
ItemUtil.mapMeta(item, meta -> {
if (meta instanceof PotionMeta potionMeta) {
potionMeta.addCustomEffect(new PotionEffect(PotionEffectType.HARM, 20, 0), true);
}
});
ThrownPotion potion = shooter.launchProjectile(ThrownPotion.class);
potion.setItem(item);
potion.teleport(location);
AreaEffectCloud cloud = world.spawn(location, AreaEffectCloud.class);
cloud.clearCustomEffects();
cloud.setSource(shooter);
cloud.setParticle(Particle.DRAGON_BREATH);
cloud.setRadius((float) this.getFireRadius(level));
cloud.setDuration(this.getFireDuration(level));
cloud.setRadiusPerTick((7.0F - cloud.getRadius()) / (float) cloud.getDuration());
cloud.addCustomEffect(new PotionEffect(PotionEffectType.HARM, 1, 1), true);
LingeringPotionSplashEvent splashEvent = new LingeringPotionSplashEvent(potion, cloud);
plugin.getPluginManager().callEvent(splashEvent);
if (splashEvent.isCancelled()) {
cloud.remove();
}
potion.remove();
}
}

View File

@ -0,0 +1,114 @@
package su.nightexpress.excellentenchants.enchantment.impl.bow;
import org.bukkit.Location;
import org.bukkit.Particle;
import org.bukkit.block.Block;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.Item;
import org.bukkit.entity.ItemFrame;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Projectile;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
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.utils.LocationUtil;
import su.nexmedia.engine.utils.values.UniParticle;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.api.enchantment.meta.Arrowed;
import su.nightexpress.excellentenchants.api.enchantment.meta.Chanced;
import su.nightexpress.excellentenchants.api.enchantment.type.BowEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.meta.ArrowImplementation;
import su.nightexpress.excellentenchants.enchantment.impl.meta.ChanceImplementation;
public class EnchantElectrifiedArrows extends ExcellentEnchant implements Chanced, Arrowed, BowEnchant {
public static final String ID = "electrified_arrows";
private static final String META_NO_ITEM_DAMAGE = "itemNoDamage";
private ArrowImplementation arrowImplementation;
private ChanceImplementation chanceImplementation;
public EnchantElectrifiedArrows(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription(Placeholders.ENCHANTMENT_CHANCE + "% chance to launch an electrified arrow.");
this.getDefaults().setLevelMax(3);
this.getDefaults().setTier(0.3);
this.getDefaults().setConflicts(EnchantEnderBow.ID, EnchantGhast.ID, EnchantBomber.ID);
}
@Override
public void loadSettings() {
super.loadSettings();
this.arrowImplementation = ArrowImplementation.create(this, UniParticle.of(Particle.FIREWORKS_SPARK));
this.chanceImplementation = ChanceImplementation.create(this,
"10.0 + " + Placeholders.ENCHANTMENT_LEVEL + " * 5");
}
@NotNull
@Override
public ArrowImplementation getArrowImplementation() {
return arrowImplementation;
}
@NotNull
@Override
public ChanceImplementation getChanceImplementation() {
return chanceImplementation;
}
@NotNull
@Override
public EnchantmentTarget getCategory() {
return EnchantmentTarget.BOW;
}
@Override
public boolean onShoot(@NotNull EntityShootBowEvent event, @NotNull LivingEntity shooter, @NotNull ItemStack bow, int level) {
return this.checkTriggerChance(level);
}
@Override
public boolean onHit(@NotNull ProjectileHitEvent event, LivingEntity user, @NotNull Projectile projectile, @NotNull ItemStack bow, int level) {
if (!this.isOurProjectile(projectile)) return false;
if (event.getHitEntity() != null || event.getHitBlock() == null) return false;
Block block = event.getHitBlock();
block.getWorld().strikeLightning(block.getLocation()).setMetadata(META_NO_ITEM_DAMAGE, new FixedMetadataValue(plugin, true));
if (this.hasVisualEffects()) {
Location center = LocationUtil.getCenter(block.getLocation());
UniParticle.blockCrack(block.getType()).play(center, 1, 0.05, 120);
UniParticle.of(Particle.FIREWORKS_SPARK).play(center, 1, 0.05, 120);
}
return false;
}
@Override
public boolean onDamage(@NotNull EntityDamageByEntityEvent event, @NotNull Projectile projectile, @NotNull LivingEntity shooter, @NotNull LivingEntity victim, @NotNull ItemStack weapon, int level) {
if (!this.isOurProjectile(projectile)) return false;
plugin.getServer().getScheduler().runTask(plugin, () -> {
if (victim.isDead()) return;
victim.setNoDamageTicks(0);
victim.getWorld().strikeLightning(victim.getLocation()).setMetadata(META_NO_ITEM_DAMAGE, new FixedMetadataValue(plugin, true));
});
return false;
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onItemDamage(EntityDamageByEntityEvent e) {
if (!e.getDamager().hasMetadata(META_NO_ITEM_DAMAGE)) return;
if (e.getEntity() instanceof Item || e.getEntity() instanceof ItemFrame) {
e.setCancelled(true);
e.getEntity().setFireTicks(0);
}
}
}

View File

@ -0,0 +1,88 @@
package su.nightexpress.excellentenchants.enchantment.impl.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.EventPriority;
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.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.api.enchantment.meta.Chanced;
import su.nightexpress.excellentenchants.api.enchantment.type.BowEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.meta.ChanceImplementation;
public class EnchantEnderBow extends ExcellentEnchant implements BowEnchant, Chanced {
public static final String ID = "ender_bow";
private ChanceImplementation chanceImplementation;
public EnchantEnderBow(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription("Shoots ender pearls instead of arrows.");
this.getDefaults().setLevelMax(1);
this.getDefaults().setTier(1.0);
this.getDefaults().setConflicts(
EnchantBomber.ID, EnchantGhast.ID,
EnchantExplosiveArrows.ID, EnchantPoisonedArrows.ID, EnchantConfusingArrows.ID,
EnchantWitheredArrows.ID, EnchantElectrifiedArrows.ID, EnchantDragonfireArrows.ID,
DarknessArrowsEnchant.ID, VampiricArrowsEnchant.ID,
EnchantHover.ID, FlareEnchant.ID,
Enchantment.ARROW_FIRE.getKey().getKey(),
Enchantment.ARROW_KNOCKBACK.getKey().getKey(),
Enchantment.ARROW_DAMAGE.getKey().getKey()
);
}
@Override
public void loadSettings() {
super.loadSettings();
this.chanceImplementation = ChanceImplementation.create(this, "100");
}
@NotNull
@Override
public ChanceImplementation getChanceImplementation() {
return chanceImplementation;
}
@Override
@NotNull
public EnchantmentTarget getCategory() {
return EnchantmentTarget.BOW;
}
@NotNull
@Override
public EventPriority getShootPriority() {
return EventPriority.LOWEST;
}
@Override
public boolean onShoot(@NotNull EntityShootBowEvent event, @NotNull LivingEntity shooter, @NotNull ItemStack bow, int level) {
if (!this.checkTriggerChance(level)) return false;
if (!(event.getProjectile() instanceof Projectile projectile)) return false;
EnderPearl pearl = shooter.launchProjectile(EnderPearl.class);
pearl.setVelocity(projectile.getVelocity());
event.setProjectile(pearl);
return true;
}
@Override
public boolean onHit(@NotNull ProjectileHitEvent event, LivingEntity user, @NotNull Projectile projectile, @NotNull ItemStack bow, int level) {
return false;
}
@Override
public boolean onDamage(@NotNull EntityDamageByEntityEvent event, @NotNull Projectile projectile, @NotNull LivingEntity shooter, @NotNull LivingEntity victim, @NotNull ItemStack weapon, int level) {
return false;
}
}

View File

@ -0,0 +1,137 @@
package su.nightexpress.excellentenchants.enchantment.impl.bow;
import org.bukkit.Particle;
import org.bukkit.World;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.*;
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.EntityShootBowEvent;
import org.bukkit.event.entity.ProjectileHitEvent;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import su.nexmedia.engine.api.config.JOption;
import su.nexmedia.engine.utils.NumberUtil;
import su.nexmedia.engine.utils.values.UniParticle;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.api.enchantment.meta.Arrowed;
import su.nightexpress.excellentenchants.api.enchantment.meta.Chanced;
import su.nightexpress.excellentenchants.api.enchantment.type.BowEnchant;
import su.nightexpress.excellentenchants.enchantment.config.EnchantScaler;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.meta.ArrowImplementation;
import su.nightexpress.excellentenchants.enchantment.impl.meta.ChanceImplementation;
public class EnchantExplosiveArrows extends ExcellentEnchant implements Chanced, Arrowed, BowEnchant {
public static final String ID = "explosive_arrows";
public static final String PLACEHOLDER_EXPLOSION_POWER = "%enchantment_explosion_power%";
private boolean explosionFireSpread;
private boolean explosionDamageItems;
private boolean explosionDamageBlocks;
private EnchantScaler explosionSize;
private ArrowImplementation arrowImplementation;
private ChanceImplementation chanceImplementation;
private Entity lastExploder;
public EnchantExplosiveArrows(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription(Placeholders.ENCHANTMENT_CHANCE + "% chance to launch an explosive arrow.");
this.getDefaults().setLevelMax(3);
this.getDefaults().setTier(0.7);
this.getDefaults().setConflicts(EnchantEnderBow.ID, EnchantGhast.ID, EnchantBomber.ID);
}
@Override
public void loadSettings() {
super.loadSettings();
this.arrowImplementation = ArrowImplementation.create(this, UniParticle.of(Particle.SMOKE_NORMAL));
this.chanceImplementation = ChanceImplementation.create(this,
"10.0 + " + Placeholders.ENCHANTMENT_LEVEL + " * 5");
this.explosionFireSpread = JOption.create("Settings.Explosion.Fire_Spread", true,
"When 'true' creates fire on nearby blocks.").read(cfg);
this.explosionDamageItems = JOption.create("Settings.Explosion.Damage_Items", false,
"When 'true' inflicts damage to items on the ground.").read(cfg);
this.explosionDamageBlocks = JOption.create("Settings.Explosion.Damage_Blocks", false,
"When 'true' allows to break blocks by explosion.").read(cfg);
this.explosionSize = EnchantScaler.read(this, "Settings.Explosion.Size",
"2.0 + " + Placeholders.ENCHANTMENT_LEVEL,
"Sets the explosion size. The more size - the bigger explosion.");
this.addPlaceholder(PLACEHOLDER_EXPLOSION_POWER, level -> NumberUtil.format(this.getExplosionSize(level)));
}
@NotNull
@Override
public ArrowImplementation getArrowImplementation() {
return arrowImplementation;
}
@NotNull
@Override
public ChanceImplementation getChanceImplementation() {
return chanceImplementation;
}
@NotNull
@Override
public EnchantmentTarget getCategory() {
return EnchantmentTarget.BOW;
}
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 onShoot(@NotNull EntityShootBowEvent event, @NotNull LivingEntity shooter, @NotNull ItemStack bow, int level) {
return this.checkTriggerChance(level);
}
@Override
public boolean onHit(@NotNull ProjectileHitEvent event, LivingEntity user, @NotNull Projectile projectile, @NotNull ItemStack bow, int level) {
if (!this.isOurProjectile(projectile)) return false;
if (projectile.getShooter() instanceof Entity entity) {
this.lastExploder = entity;
}
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, this.lastExploder);
this.lastExploder = null;
return false;
}
@Override
public boolean onDamage(@NotNull EntityDamageByEntityEvent event, @NotNull Projectile projectile, @NotNull LivingEntity shooter, @NotNull LivingEntity victim, @NotNull ItemStack weapon, int level) {
return false;
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onItemDamage(EntityDamageByEntityEvent event) {
if (event.getCause() != EntityDamageEvent.DamageCause.ENTITY_EXPLOSION) return;
if (this.explosionDamageItems) return;
if (this.lastExploder == null || this.lastExploder != event.getDamager()) return;
if (event.getEntity() instanceof Item || event.getEntity() instanceof ItemFrame) {
event.setCancelled(true);
}
}
}

View File

@ -0,0 +1,122 @@
package su.nightexpress.excellentenchants.enchantment.impl.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.EventPriority;
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.JOption;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.api.enchantment.meta.Chanced;
import su.nightexpress.excellentenchants.api.enchantment.type.BowEnchant;
import su.nightexpress.excellentenchants.enchantment.config.EnchantScaler;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.meta.ChanceImplementation;
import su.nightexpress.excellentenchants.enchantment.util.EnchantUtils;
public class EnchantGhast extends ExcellentEnchant implements BowEnchant, Chanced {
public static final String ID = "ghast";
private boolean fireSpread;
private EnchantScaler yield;
private ChanceImplementation chanceImplementation;
public EnchantGhast(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription("Shoots fireballs instead of arrows.");
this.getDefaults().setLevelMax(1);
this.getDefaults().setTier(0.3);
this.getDefaults().setConflicts(
EnchantEnderBow.ID, EnchantBomber.ID,
EnchantExplosiveArrows.ID, EnchantPoisonedArrows.ID, EnchantConfusingArrows.ID,
EnchantWitheredArrows.ID, EnchantElectrifiedArrows.ID, EnchantDragonfireArrows.ID,
DarknessArrowsEnchant.ID, VampiricArrowsEnchant.ID,
EnchantHover.ID, FlareEnchant.ID,
Enchantment.ARROW_FIRE.getKey().getKey(),
Enchantment.ARROW_KNOCKBACK.getKey().getKey(),
Enchantment.ARROW_DAMAGE.getKey().getKey()
);
}
@Override
public void loadSettings() {
super.loadSettings();
this.chanceImplementation = ChanceImplementation.create(this, "100");
this.fireSpread = JOption.create("Settings.Fire_Spread", true,
"When 'true' creates fire on nearby blocks.").read(cfg);
this.yield = EnchantScaler.read(this, "Settings.Yield", "1.0 + " + Placeholders.ENCHANTMENT_LEVEL,
"Fireball explosion size/radius. The more value = the bigger the explosion.");
}
@NotNull
@Override
public ChanceImplementation getChanceImplementation() {
return chanceImplementation;
}
public boolean isFireSpread() {
return fireSpread;
}
public float getYield(int level) {
return (float) this.yield.getValue(level);
}
@Override
@NotNull
public EnchantmentTarget getCategory() {
return EnchantmentTarget.BOW;
}
@NotNull
@Override
public EventPriority getShootPriority() {
return EventPriority.LOWEST;
}
@Override
public boolean onShoot(@NotNull EntityShootBowEvent event, @NotNull LivingEntity shooter, @NotNull ItemStack bow, int level) {
if (!this.checkTriggerChance(level)) return false;
if (!(event.getProjectile() instanceof Projectile projectile)) 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 (EnchantUtils.contains(bow, 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));
event.setProjectile(fireball);
return true;
}
@Override
public boolean onHit(@NotNull ProjectileHitEvent event, LivingEntity user, @NotNull Projectile projectile, @NotNull ItemStack bow, int level) {
return false;
}
@Override
public boolean onDamage(@NotNull EntityDamageByEntityEvent event, @NotNull Projectile projectile,
@NotNull LivingEntity shooter, @NotNull LivingEntity victim,
@NotNull ItemStack weapon, int level) {
return false;
}
}

View File

@ -0,0 +1,96 @@
package su.nightexpress.excellentenchants.enchantment.impl.bow;
import org.bukkit.Particle;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.AbstractArrow;
import org.bukkit.entity.Arrow;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Projectile;
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.potion.PotionEffectType;
import org.jetbrains.annotations.NotNull;
import su.nexmedia.engine.utils.values.UniParticle;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.api.enchantment.meta.Arrowed;
import su.nightexpress.excellentenchants.api.enchantment.meta.Chanced;
import su.nightexpress.excellentenchants.api.enchantment.meta.Potioned;
import su.nightexpress.excellentenchants.api.enchantment.type.BowEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.meta.ArrowImplementation;
import su.nightexpress.excellentenchants.enchantment.impl.meta.ChanceImplementation;
import su.nightexpress.excellentenchants.enchantment.impl.meta.PotionImplementation;
public class EnchantHover extends ExcellentEnchant implements Chanced, Arrowed, Potioned, BowEnchant {
public static final String ID = "hover";
private ArrowImplementation arrowImplementation;
private ChanceImplementation chanceImplementation;
private PotionImplementation potionImplementation;
public EnchantHover(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription(Placeholders.ENCHANTMENT_CHANCE + "% chance to launch an arrow with " + Placeholders.ENCHANTMENT_POTION_TYPE + " " + Placeholders.ENCHANTMENT_POTION_LEVEL + " (" + Placeholders.ENCHANTMENT_POTION_DURATION + "s.)");
this.getDefaults().setLevelMax(3);
this.getDefaults().setTier(0.1);
this.getDefaults().setConflicts(EnchantEnderBow.ID, EnchantGhast.ID, EnchantBomber.ID);
}
@Override
public void loadSettings() {
super.loadSettings();
this.arrowImplementation = ArrowImplementation.create(this, UniParticle.of(Particle.BUBBLE_POP));
this.chanceImplementation = ChanceImplementation.create(this,
"10.0 + " + Placeholders.ENCHANTMENT_LEVEL + " * 5");
this.potionImplementation = PotionImplementation.create(this, PotionEffectType.LEVITATION, false,
"2.5 + " + Placeholders.ENCHANTMENT_LEVEL,
Placeholders.ENCHANTMENT_LEVEL);
}
@NotNull
@Override
public ArrowImplementation getArrowImplementation() {
return arrowImplementation;
}
@NotNull
@Override
public ChanceImplementation getChanceImplementation() {
return chanceImplementation;
}
@NotNull
@Override
public PotionImplementation getPotionImplementation() {
return potionImplementation;
}
@NotNull
@Override
public EnchantmentTarget getCategory() {
return EnchantmentTarget.BOW;
}
@Override
public boolean onShoot(@NotNull EntityShootBowEvent event, @NotNull LivingEntity shooter, @NotNull ItemStack bow, int level) {
if (!(event.getProjectile() instanceof Arrow arrow)) return false;
if (!this.checkTriggerChance(level)) return false;
arrow.setPickupStatus(AbstractArrow.PickupStatus.DISALLOWED);
return arrow.addCustomEffect(this.createEffect(level), true);
}
@Override
public boolean onHit(@NotNull ProjectileHitEvent event, LivingEntity user, @NotNull Projectile projectile, @NotNull ItemStack bow, int level) {
return false;
}
@Override
public boolean onDamage(@NotNull EntityDamageByEntityEvent event, @NotNull Projectile projectile, @NotNull LivingEntity shooter, @NotNull LivingEntity victim, @NotNull ItemStack weapon, int level) {
return false;
}
}

View File

@ -0,0 +1,96 @@
package su.nightexpress.excellentenchants.enchantment.impl.bow;
import org.bukkit.Particle;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.AbstractArrow;
import org.bukkit.entity.Arrow;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Projectile;
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.potion.PotionEffectType;
import org.jetbrains.annotations.NotNull;
import su.nexmedia.engine.utils.values.UniParticle;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.api.enchantment.meta.Arrowed;
import su.nightexpress.excellentenchants.api.enchantment.meta.Chanced;
import su.nightexpress.excellentenchants.api.enchantment.meta.Potioned;
import su.nightexpress.excellentenchants.api.enchantment.type.BowEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.meta.ArrowImplementation;
import su.nightexpress.excellentenchants.enchantment.impl.meta.ChanceImplementation;
import su.nightexpress.excellentenchants.enchantment.impl.meta.PotionImplementation;
public class EnchantPoisonedArrows extends ExcellentEnchant implements Chanced, Arrowed, Potioned, BowEnchant {
public static final String ID = "poisoned_arrows";
private ArrowImplementation arrowImplementation;
private ChanceImplementation chanceImplementation;
private PotionImplementation potionImplementation;
public EnchantPoisonedArrows(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription(Placeholders.ENCHANTMENT_CHANCE + "% chance to launch an arrow with " + Placeholders.ENCHANTMENT_POTION_TYPE + " " + Placeholders.ENCHANTMENT_POTION_LEVEL + " (" + Placeholders.ENCHANTMENT_POTION_DURATION + "s.)");
this.getDefaults().setLevelMax(3);
this.getDefaults().setTier(0.1);
this.getDefaults().setConflicts(EnchantEnderBow.ID, EnchantGhast.ID, EnchantBomber.ID);
}
@Override
public void loadSettings() {
super.loadSettings();
this.arrowImplementation = ArrowImplementation.create(this, UniParticle.of(Particle.SLIME));
this.chanceImplementation = ChanceImplementation.create(this,
"25.0 + " + Placeholders.ENCHANTMENT_LEVEL + " * 5");
this.potionImplementation = PotionImplementation.create(this, PotionEffectType.POISON, false,
"2.5 + " + Placeholders.ENCHANTMENT_LEVEL,
Placeholders.ENCHANTMENT_LEVEL);
}
@NotNull
@Override
public ArrowImplementation getArrowImplementation() {
return arrowImplementation;
}
@NotNull
@Override
public ChanceImplementation getChanceImplementation() {
return chanceImplementation;
}
@NotNull
@Override
public PotionImplementation getPotionImplementation() {
return potionImplementation;
}
@NotNull
@Override
public EnchantmentTarget getCategory() {
return EnchantmentTarget.BOW;
}
@Override
public boolean onShoot(@NotNull EntityShootBowEvent event, @NotNull LivingEntity shooter, @NotNull ItemStack bow, int level) {
if (!(event.getProjectile() instanceof Arrow arrow)) return false;
if (!this.checkTriggerChance(level)) return false;
arrow.setPickupStatus(AbstractArrow.PickupStatus.DISALLOWED);
return arrow.addCustomEffect(this.createEffect(level), true);
}
@Override
public boolean onHit(@NotNull ProjectileHitEvent event, LivingEntity user, @NotNull Projectile projectile, @NotNull ItemStack bow, int level) {
return false;
}
@Override
public boolean onDamage(@NotNull EntityDamageByEntityEvent event, @NotNull Projectile projectile, @NotNull LivingEntity shooter, @NotNull LivingEntity victim, @NotNull ItemStack weapon, int level) {
return false;
}
}

View File

@ -0,0 +1,96 @@
package su.nightexpress.excellentenchants.enchantment.impl.bow;
import org.bukkit.Particle;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.AbstractArrow;
import org.bukkit.entity.Arrow;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Projectile;
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.potion.PotionEffectType;
import org.jetbrains.annotations.NotNull;
import su.nexmedia.engine.utils.values.UniParticle;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.api.enchantment.meta.Arrowed;
import su.nightexpress.excellentenchants.api.enchantment.meta.Chanced;
import su.nightexpress.excellentenchants.api.enchantment.meta.Potioned;
import su.nightexpress.excellentenchants.api.enchantment.type.BowEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.meta.ArrowImplementation;
import su.nightexpress.excellentenchants.enchantment.impl.meta.ChanceImplementation;
import su.nightexpress.excellentenchants.enchantment.impl.meta.PotionImplementation;
public class EnchantWitheredArrows extends ExcellentEnchant implements Chanced, Arrowed, Potioned, BowEnchant {
public static final String ID = "withered_arrows";
private ArrowImplementation arrowImplementation;
private ChanceImplementation chanceImplementation;
private PotionImplementation potionImplementation;
public EnchantWitheredArrows(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription(Placeholders.ENCHANTMENT_CHANCE + "% chance to launch an arrow with " + Placeholders.ENCHANTMENT_POTION_TYPE + " " + Placeholders.ENCHANTMENT_POTION_LEVEL + " (" + Placeholders.ENCHANTMENT_POTION_DURATION + "s.)");
this.getDefaults().setLevelMax(3);
this.getDefaults().setTier(0.5);
this.getDefaults().setConflicts(EnchantEnderBow.ID, EnchantGhast.ID, EnchantBomber.ID);
}
@Override
public void loadSettings() {
super.loadSettings();
this.arrowImplementation = ArrowImplementation.create(this, UniParticle.of(Particle.SPELL_WITCH));
this.chanceImplementation = ChanceImplementation.create(this,
"15.0 + " + Placeholders.ENCHANTMENT_LEVEL + " * 5.0");
this.potionImplementation = PotionImplementation.create(this, PotionEffectType.WITHER, false,
"2.5 + " + Placeholders.ENCHANTMENT_LEVEL + " * 0.75",
Placeholders.ENCHANTMENT_LEVEL);
}
@NotNull
@Override
public ArrowImplementation getArrowImplementation() {
return arrowImplementation;
}
@NotNull
@Override
public ChanceImplementation getChanceImplementation() {
return chanceImplementation;
}
@NotNull
@Override
public PotionImplementation getPotionImplementation() {
return potionImplementation;
}
@NotNull
@Override
public EnchantmentTarget getCategory() {
return EnchantmentTarget.BOW;
}
@Override
public boolean onShoot(@NotNull EntityShootBowEvent event, @NotNull LivingEntity shooter, @NotNull ItemStack bow, int level) {
if (!(event.getProjectile() instanceof Arrow arrow)) return false;
if (!this.checkTriggerChance(level)) return false;
arrow.setPickupStatus(AbstractArrow.PickupStatus.DISALLOWED);
return arrow.addCustomEffect(this.createEffect(level), true);
}
@Override
public boolean onHit(@NotNull ProjectileHitEvent event, LivingEntity user, @NotNull Projectile projectile, @NotNull ItemStack bow, int level) {
return false;
}
@Override
public boolean onDamage(@NotNull EntityDamageByEntityEvent event, @NotNull Projectile projectile, @NotNull LivingEntity shooter, @NotNull LivingEntity victim, @NotNull ItemStack weapon, int level) {
return false;
}
}

View File

@ -0,0 +1,121 @@
package su.nightexpress.excellentenchants.enchantment.impl.bow;
import org.bukkit.Material;
import org.bukkit.Particle;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.data.Directional;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.Arrow;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.entity.Projectile;
import org.bukkit.event.EventPriority;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.entity.EntityShootBowEvent;
import org.bukkit.event.entity.ProjectileHitEvent;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import su.nexmedia.engine.utils.values.UniParticle;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.api.enchantment.meta.Arrowed;
import su.nightexpress.excellentenchants.api.enchantment.meta.Chanced;
import su.nightexpress.excellentenchants.api.enchantment.type.BowEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.meta.ArrowImplementation;
import su.nightexpress.excellentenchants.enchantment.impl.meta.ChanceImplementation;
public class FlareEnchant extends ExcellentEnchant implements Chanced, Arrowed, BowEnchant {
public static final String ID = "flare";
private ChanceImplementation chanceImplementation;
private ArrowImplementation arrowImplementation;
public FlareEnchant(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription(Placeholders.ENCHANTMENT_CHANCE + "% chance to create a torch where arrow lands.");
this.getDefaults().setLevelMax(1);
this.getDefaults().setTier(0.4);
}
@Override
public void loadSettings() {
super.loadSettings();
this.chanceImplementation = ChanceImplementation.create(this, "100.0");
this.arrowImplementation = ArrowImplementation.create(this, UniParticle.of(Particle.FIREWORKS_SPARK));
}
@NotNull
@Override
public EnchantmentTarget getCategory() {
return EnchantmentTarget.BOW;
}
@NotNull
@Override
public EventPriority getHitPriority() {
return EventPriority.HIGHEST;
}
@NotNull
@Override
public Chanced getChanceImplementation() {
return this.chanceImplementation;
}
@NotNull
@Override
public ArrowImplementation getArrowImplementation() {
return this.arrowImplementation;
}
@Override
public boolean onShoot(@NotNull EntityShootBowEvent event, @NotNull LivingEntity shooter, @NotNull ItemStack bow, int level) {
if (!(event.getProjectile() instanceof Arrow arrow)) return false;
if (!this.checkTriggerChance(level)) return false;
this.addData(arrow);
return true;
}
@Override
public boolean onHit(@NotNull ProjectileHitEvent e, LivingEntity user, @NotNull Projectile projectile, @NotNull ItemStack bow, int level) {
Block block = e.getHitBlock();
if (block == null) return false;
BlockFace face = e.getHitBlockFace();
if (face == null || face == BlockFace.DOWN) return false;
Block relative = block.getRelative(face);
if (!relative.getType().isAir()) return false;
if (projectile.getShooter() instanceof Player player) {
BlockPlaceEvent event = new BlockPlaceEvent(relative, relative.getState(), block, new ItemStack(Material.TORCH), player,true, EquipmentSlot.HAND);
plugin.getPluginManager().callEvent(event);
if (event.isCancelled() || !event.canBuild()) return false;
}
if (face == BlockFace.UP) {
relative.setType(Material.TORCH);
}
else {
relative.setType(Material.WALL_TORCH);
Directional directional = (Directional) relative.getBlockData();
directional.setFacing(face);
relative.setBlockData(directional, true);
}
return false;
}
@Override
public boolean onDamage(@NotNull EntityDamageByEntityEvent event, @NotNull Projectile projectile, @NotNull LivingEntity shooter, @NotNull LivingEntity victim, @NotNull ItemStack weapon, int level) {
return false;
}
}

View File

@ -0,0 +1,95 @@
package su.nightexpress.excellentenchants.enchantment.impl.bow;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Projectile;
import org.bukkit.event.EventPriority;
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.util.Vector;
import org.jetbrains.annotations.NotNull;
import su.nexmedia.engine.utils.NumberUtil;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.api.enchantment.meta.Chanced;
import su.nightexpress.excellentenchants.api.enchantment.type.BowEnchant;
import su.nightexpress.excellentenchants.enchantment.config.EnchantScaler;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.meta.ChanceImplementation;
public class SniperEnchant extends ExcellentEnchant implements BowEnchant, Chanced {
public static final String ID = "sniper";
private static final String PLACEHOLDER_PROJECTILE_SPEED = "%enchantment_projectile_speed%";
private ChanceImplementation chanceImplementation;
private EnchantScaler speedModifier;
public SniperEnchant(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription("Increases projectile speed by " + PLACEHOLDER_PROJECTILE_SPEED + "%");
this.getDefaults().setLevelMax(5);
this.getDefaults().setTier(0.3);
}
@Override
public void loadSettings() {
super.loadSettings();
this.chanceImplementation = ChanceImplementation.create(this, "100.0");
this.speedModifier = EnchantScaler.read(this, "Settings.Speed_Modifier",
"1.0 + " + Placeholders.ENCHANTMENT_LEVEL + " / 5.0", "Sets projectile's speed modifier.");
this.addPlaceholder(PLACEHOLDER_PROJECTILE_SPEED, level -> NumberUtil.format(this.getSpeedModifier(level) * 100D));
}
@NotNull
@Override
public Chanced getChanceImplementation() {
return this.chanceImplementation;
}
public double getSpeedModifier(int level) {
return this.speedModifier.getValue(level);
}
@NotNull
@Override
public EnchantmentTarget getCategory() {
return EnchantmentTarget.BOW;
}
@NotNull
@Override
public EventPriority getShootPriority() {
return EventPriority.LOWEST;
}
@Override
public boolean onShoot(@NotNull EntityShootBowEvent event, @NotNull LivingEntity shooter, @NotNull ItemStack bow, int level) {
if (!this.checkTriggerChance(level)) return false;
double modifier = this.getSpeedModifier(level);
Entity entity = event.getProjectile();
Vector vector = entity.getVelocity();
entity.setVelocity(vector.multiply(modifier));
return true;
}
@Override
public boolean onHit(@NotNull ProjectileHitEvent event, LivingEntity user, @NotNull Projectile projectile, @NotNull ItemStack bow, int level) {
return false;
}
@Override
public boolean onDamage(@NotNull EntityDamageByEntityEvent event, @NotNull Projectile projectile, @NotNull LivingEntity shooter, @NotNull LivingEntity victim, @NotNull ItemStack weapon, int level) {
return false;
}
}

View File

@ -0,0 +1,127 @@
package su.nightexpress.excellentenchants.enchantment.impl.bow;
import org.bukkit.Color;
import org.bukkit.Particle;
import org.bukkit.attribute.Attribute;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.Arrow;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Projectile;
import org.bukkit.event.EventPriority;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.entity.EntityRegainHealthEvent;
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.utils.EntityUtil;
import su.nexmedia.engine.utils.NumberUtil;
import su.nexmedia.engine.utils.values.UniParticle;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.api.enchantment.meta.Arrowed;
import su.nightexpress.excellentenchants.api.enchantment.meta.Chanced;
import su.nightexpress.excellentenchants.api.enchantment.type.BowEnchant;
import su.nightexpress.excellentenchants.enchantment.config.EnchantScaler;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.meta.ArrowImplementation;
import su.nightexpress.excellentenchants.enchantment.impl.meta.ChanceImplementation;
public class VampiricArrowsEnchant extends ExcellentEnchant implements BowEnchant, Arrowed, Chanced {
public static final String ID = "vampiric_arrows";
public static final String PLACEHOLDER_HEAL_AMOUNT = "%heal_amount%";
private ArrowImplementation arrowImplementation;
private ChanceImplementation chanceImplementation;
private EnchantScaler healAmount;
public VampiricArrowsEnchant(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription(Placeholders.ENCHANTMENT_CHANCE + "% chance to restore " + PLACEHOLDER_HEAL_AMOUNT + "❤ on arrow hit.");
this.getDefaults().setLevelMax(3);
this.getDefaults().setTier(0.3);
this.getDefaults().setConflicts(EnchantEnderBow.ID, EnchantGhast.ID, EnchantBomber.ID);
}
@Override
public void loadSettings() {
super.loadSettings();
this.arrowImplementation = ArrowImplementation.create(this, UniParticle.redstone(Color.RED, 1f));
this.chanceImplementation = ChanceImplementation.create(this, "20.0 * " + Placeholders.ENCHANTMENT_LEVEL);
this.healAmount = EnchantScaler.read(this, "Settings.Heal_Amount",
Placeholders.ENCHANTMENT_LEVEL,
"Amount of health to be restored on hit.");
this.addPlaceholder(PLACEHOLDER_HEAL_AMOUNT, level -> NumberUtil.format(this.getHealAmount(level)));
}
@NotNull
@Override
public EnchantmentTarget getCategory() {
return EnchantmentTarget.BOW;
}
@NotNull
@Override
public Arrowed getArrowImplementation() {
return this.arrowImplementation;
}
@NotNull
@Override
public Chanced getChanceImplementation() {
return this.chanceImplementation;
}
public double getHealAmount(int level) {
return this.healAmount.getValue(level);
}
@NotNull
@Override
public EventPriority getDamagePriority() {
return EventPriority.HIGHEST;
}
@Override
public boolean onShoot(@NotNull EntityShootBowEvent event, @NotNull LivingEntity shooter, @NotNull ItemStack bow, int level) {
if (!(event.getProjectile() instanceof Arrow arrow)) return false;
if (!this.checkTriggerChance(level)) return false;
this.addData(arrow);
return true;
}
@Override
public boolean onHit(@NotNull ProjectileHitEvent event, LivingEntity user, @NotNull Projectile projectile, @NotNull ItemStack bow, int level) {
return false;
}
@Override
public boolean onDamage(@NotNull EntityDamageByEntityEvent event, @NotNull Projectile projectile, @NotNull LivingEntity shooter, @NotNull LivingEntity victim, @NotNull ItemStack weapon, int level) {
if (shooter.isDead() || shooter.getHealth() <= 0D) return false;
double healAmount = this.getHealAmount(level);
if (healAmount <= 0D) return false;
double health = shooter.getHealth();
double maxHealth = EntityUtil.getAttribute(shooter, Attribute.GENERIC_MAX_HEALTH);
if (health >= maxHealth) return false;
EntityRegainHealthEvent healthEvent = new EntityRegainHealthEvent(shooter, healAmount, EntityRegainHealthEvent.RegainReason.CUSTOM);
plugin.getPluginManager().callEvent(healthEvent);
if (healthEvent.isCancelled()) return false;
shooter.setHealth(Math.min(maxHealth, health + healAmount));
if (this.hasVisualEffects()) {
UniParticle.of(Particle.HEART).play(shooter.getEyeLocation(), 0.25f, 0.15f, 5);
}
return false;
}
}

View File

@ -0,0 +1,40 @@
package su.nightexpress.excellentenchants.enchantment.impl.fishing;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.event.player.PlayerFishEvent;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.api.enchantment.type.FishingEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
public class AutoReelEnchant extends ExcellentEnchant implements FishingEnchant {
public static final String ID = "auto_reel";
public AutoReelEnchant(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription("Automatically reels in a hook on bite.");
this.getDefaults().setLevelMax(1);
this.getDefaults().setTier(1.0);
}
@NotNull
@Override
public EnchantmentTarget getCategory() {
return EnchantmentTarget.FISHING_ROD;
}
@Override
public boolean onFishing(@NotNull PlayerFishEvent event, @NotNull ItemStack item, int level) {
if (event.getState() != PlayerFishEvent.State.BITE) return false;
this.plugin.runTask(task -> {
if (event.isCancelled()) return;
plugin.getEnchantNMS().sendAttackPacket(event.getPlayer(), 0);
plugin.getEnchantNMS().retrieveHook(event.getHook(), item);
});
return true;
}
}

View File

@ -0,0 +1,70 @@
package su.nightexpress.excellentenchants.enchantment.impl.fishing;
import org.bukkit.Particle;
import org.bukkit.Sound;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.Drowned;
import org.bukkit.entity.FishHook;
import org.bukkit.event.player.PlayerFishEvent;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import su.nexmedia.engine.utils.values.UniParticle;
import su.nexmedia.engine.utils.values.UniSound;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.api.enchantment.meta.Chanced;
import su.nightexpress.excellentenchants.api.enchantment.type.FishingEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.meta.ChanceImplementation;
public class CurseOfDrownedEnchant extends ExcellentEnchant implements FishingEnchant, Chanced {
public static final String ID = "curse_of_drowned";
private ChanceImplementation chanceImplementation;
public CurseOfDrownedEnchant(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription(Placeholders.ENCHANTMENT_CHANCE + "% chance to fish up a Drowned Zombie.");
this.getDefaults().setLevelMax(5);
this.getDefaults().setTier(0D);
}
@Override
public void loadSettings() {
super.loadSettings();
this.chanceImplementation = ChanceImplementation.create(this,
"5.0 + " + Placeholders.ENCHANTMENT_LEVEL);
}
@NotNull
@Override
public ChanceImplementation getChanceImplementation() {
return chanceImplementation;
}
@NotNull
@Override
public EnchantmentTarget getCategory() {
return EnchantmentTarget.FISHING_ROD;
}
@Override
public boolean onFishing(@NotNull PlayerFishEvent event, @NotNull ItemStack item, int level) {
if (event.getState() != PlayerFishEvent.State.CAUGHT_FISH) return false;
if (!this.checkTriggerChance(level)) return false;
FishHook hook = event.getHook();
Drowned drowned = hook.getWorld().spawn(hook.getLocation(), Drowned.class);
hook.setHookedEntity(drowned);
hook.pullHookedEntity();
event.setCancelled(true);
if (this.hasVisualEffects()) {
UniParticle.of(Particle.WATER_SPLASH).play(hook.getLocation(), 0.5, 0.1, 50);
UniSound.of(Sound.ENTITY_DROWNED_AMBIENT).play(event.getPlayer());
}
return true;
}
}

View File

@ -0,0 +1,66 @@
package su.nightexpress.excellentenchants.enchantment.impl.fishing;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.Item;
import org.bukkit.event.EventPriority;
import org.bukkit.event.player.PlayerFishEvent;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.api.enchantment.meta.Chanced;
import su.nightexpress.excellentenchants.api.enchantment.type.FishingEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.meta.ChanceImplementation;
public class DoubleCatchEnchant extends ExcellentEnchant implements FishingEnchant, Chanced {
public static final String ID = "double_catch";
private ChanceImplementation chanceImplementation;
public DoubleCatchEnchant(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription("Increases amount of caught item by x2 with " + Placeholders.ENCHANTMENT_CHANCE + "% chance.");
this.getDefaults().setLevelMax(3);
this.getDefaults().setTier(0.5);
}
@Override
public void loadSettings() {
super.loadSettings();
this.chanceImplementation = ChanceImplementation.create(this,
"10.0 * " + Placeholders.ENCHANTMENT_LEVEL);
}
@NotNull
@Override
public EnchantmentTarget getCategory() {
return EnchantmentTarget.FISHING_ROD;
}
@NotNull
@Override
public EventPriority getFishingPriority() {
return EventPriority.HIGHEST;
}
@Override
@NotNull
public ChanceImplementation getChanceImplementation() {
return this.chanceImplementation;
}
@Override
public boolean onFishing(@NotNull PlayerFishEvent event, @NotNull ItemStack item, int level) {
if (event.getState() != PlayerFishEvent.State.CAUGHT_FISH) return false;
if (!(event.getCaught() instanceof Item drop)) return false;
if (!this.checkTriggerChance(level)) return false;
ItemStack stack = drop.getItemStack();
stack.setAmount(Math.min(stack.getMaxStackSize(), stack.getAmount() * 2));
drop.setItemStack(stack);
return true;
}
}

View File

@ -0,0 +1,68 @@
package su.nightexpress.excellentenchants.enchantment.impl.fishing;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.FishHook;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.entity.ProjectileLaunchEvent;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import su.nexmedia.engine.api.manager.EventListener;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.api.enchantment.type.GenericEnchant;
import su.nightexpress.excellentenchants.enchantment.config.EnchantScaler;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.util.EnchantUtils;
public class RiverMasterEnchant extends ExcellentEnchant implements GenericEnchant, EventListener {
public static final String ID = "river_master";
private EnchantScaler distanceMod;
public RiverMasterEnchant(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription("Increases casting distance.");
this.getDefaults().setLevelMax(5);
this.getDefaults().setTier(0.1);
}
@Override
public void loadSettings() {
super.loadSettings();
this.distanceMod = EnchantScaler.read(this, "Settings.Distance_Modifier",
"1.25 + " + Placeholders.ENCHANTMENT_LEVEL + " / 5",
"Multiplies the casted fish hook's velocity by specified value.",
"Setting too high values will result in hook auto removal by vanilla game/server mechanics.");
}
@NotNull
@Override
public EnchantmentTarget getCategory() {
return EnchantmentTarget.FISHING_ROD;
}
public double getDistanceMod(int level) {
return this.distanceMod.getValue(level);
}
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onHookLaunch(ProjectileLaunchEvent event) {
if (!(event.getEntity() instanceof FishHook hook)) return;
if (!(hook.getShooter() instanceof Player player)) return;
ItemStack rod = EnchantUtils.getFishingRod(player);
if (rod == null) return;
int level = EnchantUtils.getLevel(rod, this.getBackend());
if (level < 1) return;
if (this.isOutOfCharges(rod)) return;
hook.setVelocity(hook.getVelocity().multiply(this.getDistanceMod(level)));
this.consumeCharges(rod, level);
}
}

View File

@ -0,0 +1,59 @@
package su.nightexpress.excellentenchants.enchantment.impl.fishing;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.event.player.PlayerFishEvent;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import su.nexmedia.engine.utils.NumberUtil;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.api.enchantment.type.FishingEnchant;
import su.nightexpress.excellentenchants.enchantment.config.EnchantScaler;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
public class SeasonedAnglerEnchant extends ExcellentEnchant implements FishingEnchant {
public static final String ID = "seasoned_angler";
private EnchantScaler expMod;
public SeasonedAnglerEnchant(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription("Increases amount of XP gained from fishing by " + Placeholders.GENERIC_AMOUNT + "%.");
this.getDefaults().setLevelMax(4);
this.getDefaults().setTier(0.1);
}
@Override
public void loadSettings() {
super.loadSettings();
this.expMod = EnchantScaler.read(this, "Settings.Exp_Percent",
"25.0 * " + Placeholders.ENCHANTMENT_LEVEL,
"Amount (in percent) of additional XP from fishing.");
this.addPlaceholder(Placeholders.GENERIC_AMOUNT, level -> NumberUtil.format(this.getExpPercent(level)));
}
public int getExpPercent(int level) {
return (int) this.expMod.getValue(level);
}
@NotNull
@Override
public EnchantmentTarget getCategory() {
return EnchantmentTarget.FISHING_ROD;
}
@Override
public boolean onFishing(@NotNull PlayerFishEvent event, @NotNull ItemStack item, int level) {
if (event.getState() != PlayerFishEvent.State.CAUGHT_FISH) return false;
if (event.getExpToDrop() == 0) return false;
int expDrop = event.getExpToDrop();
int expPercent = this.getExpPercent(level);
int expModified = (int) Math.ceil(expDrop * (1D + expPercent / 100D));
event.setExpToDrop(expModified);
return true;
}
}

View File

@ -0,0 +1,83 @@
package su.nightexpress.excellentenchants.enchantment.impl.fishing;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.Item;
import org.bukkit.event.EventPriority;
import org.bukkit.event.player.PlayerFishEvent;
import org.bukkit.inventory.CookingRecipe;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.api.enchantment.meta.Chanced;
import su.nightexpress.excellentenchants.api.enchantment.type.FishingEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.meta.ChanceImplementation;
import java.util.HashSet;
import java.util.Set;
public class SurvivalistEnchant extends ExcellentEnchant implements FishingEnchant, Chanced {
public static final String ID = "survivalist";
private final Set<CookingRecipe<?>> cookingRecipes;
private ChanceImplementation chanceImplementation;
public SurvivalistEnchant(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription("Automatically cooks fish if what is caught is raw.");
this.getDefaults().setLevelMax(1);
this.getDefaults().setTier(0.4);
this.cookingRecipes = new HashSet<>();
}
@Override
public void loadSettings() {
super.loadSettings();
this.chanceImplementation = ChanceImplementation.create(this, "100");
this.cookingRecipes.clear();
this.plugin.getServer().recipeIterator().forEachRemaining(recipe -> {
if (recipe instanceof CookingRecipe<?> cookingRecipe) {
this.cookingRecipes.add(cookingRecipe);
}
});
}
@NotNull
@Override
public ChanceImplementation getChanceImplementation() {
return chanceImplementation;
}
@NotNull
@Override
public EnchantmentTarget getCategory() {
return EnchantmentTarget.FISHING_ROD;
}
@NotNull
@Override
public EventPriority getFishingPriority() {
return EventPriority.HIGH;
}
@Override
public boolean onFishing(@NotNull PlayerFishEvent event, @NotNull ItemStack item, int level) {
if (event.getState() != PlayerFishEvent.State.CAUGHT_FISH) return false;
if (!this.checkTriggerChance(level)) return false;
if (!(event.getCaught() instanceof Item drop)) return false;
ItemStack stack = drop.getItemStack();
CookingRecipe<?> recipe = this.cookingRecipes.stream().filter(r -> r.getInput().isSimilar(stack)).findFirst().orElse(null);
if (recipe == null) return false;
ItemStack cooked = recipe.getResult();
cooked.setAmount(stack.getAmount());
drop.setItemStack(cooked);
return false;
}
}

View File

@ -0,0 +1,87 @@
package su.nightexpress.excellentenchants.enchantment.impl.meta;
import org.bukkit.NamespacedKey;
import org.bukkit.Particle;
import org.bukkit.entity.Projectile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import su.nexmedia.engine.api.config.JOption;
import su.nexmedia.engine.api.config.JYML;
import su.nexmedia.engine.utils.PDCUtil;
import su.nexmedia.engine.utils.values.UniParticle;
import su.nightexpress.excellentenchants.ExcellentEnchantsAPI;
import su.nightexpress.excellentenchants.api.enchantment.meta.Arrowed;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.task.ArrowTrailsTask;
import java.util.Optional;
public final class ArrowImplementation implements Arrowed {
private final ExcellentEnchant enchant;
private final NamespacedKey projectileKey;
private final UniParticle trailParticle;
private ArrowImplementation(@NotNull ExcellentEnchant enchant, @Nullable UniParticle trailParticle) {
this.enchant = enchant;
this.projectileKey = new NamespacedKey(ExcellentEnchantsAPI.PLUGIN, "arrow.enchant_id");
this.trailParticle = trailParticle;
}
@NotNull
public static ArrowImplementation create(@NotNull ExcellentEnchant enchant) {
return create(enchant, UniParticle.of(Particle.REDSTONE));
}
@NotNull
public static ArrowImplementation create(@NotNull ExcellentEnchant enchant, @NotNull UniParticle particle) {
JYML cfg = enchant.getConfig();
UniParticle effect = new JOption<>("Settings.Arrow.Trail_Effect",
(cfg1, path, def) -> UniParticle.read(cfg1, path),
particle,
"Sets particle effect for the arrow trail of this enchantment."
).setWriter((cfg1, path, particle1) -> particle1.write(cfg1, path)).read(cfg);
return new ArrowImplementation(enchant, effect);
}
@Override
@NotNull
public Arrowed getArrowImplementation() {
return this;
}
@Override
public void addTrail(@NotNull Projectile projectile) {
if (!this.enchant.hasVisualEffects()) return;
if (this.getTrailParticle().isEmpty()) return;
this.getTrailParticle().ifPresent(particle -> {
ArrowTrailsTask.add(projectile, particle);
});
}
@NotNull
@Override
public Optional<UniParticle> getTrailParticle() {
return trailParticle == null ? Optional.empty() : Optional.of(trailParticle);
}
@NotNull
public NamespacedKey getProjectileKey() {
return projectileKey;
}
@Override
public void addData(@NotNull Projectile projectile) {
PDCUtil.set(projectile, this.getProjectileKey(), this.enchant.getId());
}
@Override
public boolean isOurProjectile(@NotNull Projectile projectile) {
String enchantId = PDCUtil.getString(projectile, this.getProjectileKey()).orElse(null);
return this.enchant.getId().equalsIgnoreCase(enchantId);
}
}

View File

@ -0,0 +1,45 @@
package su.nightexpress.excellentenchants.enchantment.impl.meta;
import org.jetbrains.annotations.NotNull;
import su.nexmedia.engine.utils.random.Rnd;
import su.nightexpress.excellentenchants.api.enchantment.meta.Chanced;
import su.nightexpress.excellentenchants.enchantment.config.EnchantScaler;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
public final class ChanceImplementation implements Chanced {
//private final ExcellentEnchant enchant;
private final EnchantScaler triggerChance;
private ChanceImplementation(@NotNull ExcellentEnchant enchant, @NotNull EnchantScaler triggerChance) {
//this.enchant = enchant;
this.triggerChance = triggerChance;
}
@NotNull
public static ChanceImplementation create(@NotNull ExcellentEnchant enchant) {
return create(enchant, "100");
}
@NotNull
public static ChanceImplementation create(@NotNull ExcellentEnchant enchant, @NotNull String def) {
return new ChanceImplementation(enchant, EnchantScaler.read(enchant, "Settings.Trigger_Chance", def,
"A chance that this enchantment will be triggered."));
}
@Override
@NotNull
public Chanced getChanceImplementation() {
return this;
}
@Override
public double getTriggerChance(int level) {
return this.triggerChance.getValue(level);
}
@Override
public boolean checkTriggerChance(int level) {
return Rnd.chance(this.getTriggerChance(level));
}
}

View File

@ -0,0 +1,56 @@
package su.nightexpress.excellentenchants.enchantment.impl.meta;
import org.jetbrains.annotations.NotNull;
import su.nightexpress.excellentenchants.api.enchantment.meta.Periodic;
import su.nightexpress.excellentenchants.enchantment.config.EnchantScaler;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
public class PeriodImplementation implements Periodic {
private final EnchantScaler triggerInterval;
private long nextTriggerTime;
public PeriodImplementation(@NotNull ExcellentEnchant enchant, @NotNull EnchantScaler triggerInterval) {
this.triggerInterval = triggerInterval;
this.updateTriggerTime();
}
@NotNull
public static PeriodImplementation create(@NotNull ExcellentEnchant enchant) {
return create(enchant, "100");
}
@NotNull
public static PeriodImplementation create(@NotNull ExcellentEnchant enchant, @NotNull String def) {
return new PeriodImplementation(enchant, EnchantScaler.read(enchant, "Settings.Trigger_Interval", def,
"Sets how often (in ticks) this enchantment will be triggered.",
"20 ticks = 1 second."));
}
@NotNull
@Override
public Periodic getPeriodImplementation() {
return this;
}
@Override
public long getInterval() {
return (long) this.triggerInterval.getValue(1);
}
@Override
public long getNextTriggerTime() {
return nextTriggerTime;
}
@Override
public boolean isTriggerTime() {
return System.currentTimeMillis() >= this.getNextTriggerTime();
}
@Override
public void updateTriggerTime() {
this.nextTriggerTime = System.currentTimeMillis() + this.getInterval() * 50L - 100L;
}
}

View File

@ -0,0 +1,91 @@
package su.nightexpress.excellentenchants.enchantment.impl.meta;
import org.bukkit.entity.LivingEntity;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.jetbrains.annotations.NotNull;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.api.enchantment.meta.Potioned;
import su.nightexpress.excellentenchants.config.Config;
import su.nightexpress.excellentenchants.enchantment.config.EnchantScaler;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
public final class PotionImplementation implements Potioned {
private final ExcellentEnchant enchant;
private final PotionEffectType effectType;
private final EnchantScaler duration;
private final EnchantScaler amplifier;
private final boolean isPermanent;
private PotionImplementation(@NotNull ExcellentEnchant enchant,
@NotNull PotionEffectType effectType, boolean isPermanent,
@NotNull EnchantScaler duration, @NotNull EnchantScaler amplifier) {
this.enchant = enchant;
this.effectType = effectType;
this.duration = duration;
this.amplifier = amplifier;
this.isPermanent = isPermanent;
}
@Override
@NotNull
public Potioned getPotionImplementation() {
return this;
}
public static PotionImplementation create(@NotNull ExcellentEnchant enchant, @NotNull PotionEffectType type, boolean isPermanent) {
return create(enchant, type, isPermanent, "5 * " + Placeholders.ENCHANTMENT_LEVEL, Placeholders.ENCHANTMENT_LEVEL);
}
public static PotionImplementation create(@NotNull ExcellentEnchant enchant,
@NotNull PotionEffectType type, boolean isPermanent,
@NotNull String duration, @NotNull String amplifier) {
EnchantScaler durationScale = EnchantScaler.read(enchant, "Settings.Potion_Effect.Duration", duration,
"Potion effect duration (in seconds). This setting is useless for 'permanent' effects.");
EnchantScaler amplifierScale = EnchantScaler.read(enchant, "Settings.Potion_Effect.Level", amplifier,
"Potion effect level.");
return new PotionImplementation(enchant, type, isPermanent, durationScale, amplifierScale);
}
@Override
public boolean isPermanent() {
return this.isPermanent;
}
@NotNull
public PotionEffectType getEffectType() {
return this.effectType;
}
public int getEffectDuration(int level) {
if (this.isPermanent()) {
int duration = Config.TASKS_PASSIVE_ENCHANTS_TRIGGER_INTERVAL.get().intValue() * 2;
if (this.getEffectType().getName().equalsIgnoreCase(PotionEffectType.NIGHT_VISION.getName()) && duration < 600) {
duration += 30 * 20;
}
return duration;
}
return (int) (this.duration.getValue(level) * 20);
}
public int getEffectAmplifier(int level) {
return (int) this.amplifier.getValue(level);
}
@NotNull
public PotionEffect createEffect(int level) {
int duration = this.getEffectDuration(level);
int amplifier = Math.max(0, this.getEffectAmplifier(level) - 1);
return new PotionEffect(this.getEffectType(), duration, amplifier, false, this.enchant.hasVisualEffects());
}
public boolean addEffect(@NotNull LivingEntity target, int level) {
target.addPotionEffect(this.createEffect(level));
return true;
}
}

View File

@ -0,0 +1,150 @@
package su.nightexpress.excellentenchants.enchantment.impl.tool;
import org.bukkit.block.Block;
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.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.jetbrains.annotations.NotNull;
import su.nexmedia.engine.api.manager.EventListener;
import su.nexmedia.engine.utils.NumberUtil;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.api.enchantment.meta.Chanced;
import su.nightexpress.excellentenchants.api.enchantment.type.BlockBreakEnchant;
import su.nightexpress.excellentenchants.enchantment.config.EnchantScaler;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.meta.ChanceImplementation;
import su.nightexpress.excellentenchants.api.enchantment.ItemCategory;
import su.nightexpress.excellentenchants.enchantment.util.EnchantUtils;
import su.nightexpress.excellentenchants.hook.impl.NoCheatPlusHook;
import java.util.List;
public class BlastMiningEnchant extends ExcellentEnchant implements Chanced, BlockBreakEnchant, EventListener {
public static final String ID = "blast_mining";
public static final String PLACEHOLDER_EXPLOSION_POWER = "%enchantment_explosion_power%";
private EnchantScaler explosionPower;
private EnchantScaler minBlockStrength;
private ChanceImplementation chanceImplementation;
private int explodeLevel;
public BlastMiningEnchant(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription(Placeholders.ENCHANTMENT_CHANCE + "% chance to mine blocks by explosion.");
this.getDefaults().setLevelMax(5);
this.getDefaults().setTier(1.0);
this.getDefaults().setConflicts(VeinminerEnchant.ID, TunnelEnchant.ID);
}
@Override
public void loadSettings() {
super.loadSettings();
this.chanceImplementation = ChanceImplementation.create(this,
"20.0 * " + Placeholders.ENCHANTMENT_LEVEL);
this.explosionPower = EnchantScaler.read(this, "Settings.Explosion.Power",
"3.0 + (" + Placeholders.ENCHANTMENT_LEVEL + " - 1.0 * 0.25)",
"Explosion power. The more power = the more blocks (area) to explode.");
this.minBlockStrength = EnchantScaler.read(this, "Settings.Min_Block_Strength",
"1.5 - " + Placeholders.ENCHANTMENT_LEVEL + " / 10",
"Minimal block strength value for the enchantment to have effect.",
"Block strength value is how long it takes to break the block by a hand.",
"For example, a Stone has 3.0 strength.");
this.addPlaceholder(PLACEHOLDER_EXPLOSION_POWER, level -> NumberUtil.format(this.getExplosionPower(level)));
}
@NotNull
@Override
public ChanceImplementation getChanceImplementation() {
return chanceImplementation;
}
public double getExplosionPower(int level) {
return this.explosionPower.getValue(level);
}
public float getMinBlockStrength(int level) {
return (float) minBlockStrength.getValue(level);
}
private boolean isHardEnough(@NotNull Block block, int level) {
float strength = block.getType().getHardness();
return (strength >= this.getMinBlockStrength(level));
}
@Override
@NotNull
public ItemCategory[] getFitItemTypes() {
return new ItemCategory[]{ItemCategory.PICKAXE};
}
@Override
@NotNull
public EnchantmentTarget getCategory() {
return EnchantmentTarget.TOOL;
}
@Override
public boolean onBreak(@NotNull BlockBreakEvent event, @NotNull LivingEntity entity, @NotNull ItemStack item, int level) {
if (!(entity instanceof Player player)) return false;
if (EnchantUtils.isBusy()) return false;
Block block = event.getBlock();
if (!this.isHardEnough(block, level)) return false;
if (!this.checkTriggerChance(level)) return false;
float power = (float) this.getExplosionPower(level);
this.explodeLevel = level;
NoCheatPlusHook.exemptBlocks(player);
boolean exploded = block.getWorld().createExplosion(block.getLocation(), power, false, true, player);
NoCheatPlusHook.unexemptBlocks(player);
this.explodeLevel = -1;
return exploded;
}
// Process explosion event to mine blocks.
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onBlastExplosionEvent(EntityExplodeEvent event) {
if (this.explodeLevel <= 0) return;
if (!(event.getEntity() instanceof Player player)) return;
List<Block> blockList = event.blockList();
blockList.forEach(block -> {
if (block.getLocation().equals(event.getLocation()) || !this.isHardEnough(block, this.explodeLevel)) return;
EnchantUtils.safeBusyBreak(player, block);
});
blockList.clear();
}
// Do not damage around entities by 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(this.explodeLevel > 0);
}
// Do not reduce item durability for 'exploded' blocks.
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onBlastExplosionItemDamage(PlayerItemDamageEvent e) {
e.setCancelled(this.explodeLevel > 0);
}
}

View File

@ -0,0 +1,85 @@
package su.nightexpress.excellentenchants.enchantment.impl.tool;
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.manager.EventListener;
import su.nexmedia.engine.utils.NumberUtil;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.api.enchantment.meta.Chanced;
import su.nightexpress.excellentenchants.api.enchantment.type.GenericEnchant;
import su.nightexpress.excellentenchants.enchantment.config.EnchantScaler;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.meta.ChanceImplementation;
import su.nightexpress.excellentenchants.enchantment.util.EnchantUtils;
public class CurseOfBreakingEnchant extends ExcellentEnchant implements GenericEnchant, EventListener, Chanced {
public static final String ID = "curse_of_breaking";
public static final String PLACEHOLDER_DURABILITY_AMOUNT = "%enchantment_durability_amount%";
private EnchantScaler durabilityAmount;
private ChanceImplementation chanceImplementation;
public CurseOfBreakingEnchant(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription(Placeholders.ENCHANTMENT_CHANCE + "% chance to consume extra " + PLACEHOLDER_DURABILITY_AMOUNT + " durability points.");
this.getDefaults().setLevelMax(3);
this.getDefaults().setTier(0D);
}
@Override
public void loadSettings() {
super.loadSettings();
this.chanceImplementation = ChanceImplementation.create(this,
"10.0 * " + Placeholders.ENCHANTMENT_LEVEL);
this.durabilityAmount = EnchantScaler.read(this, "Settings.Durability_Amount",
Placeholders.ENCHANTMENT_LEVEL,
"Amount of durability points to be taken from the item.");
this.addPlaceholder(PLACEHOLDER_DURABILITY_AMOUNT, level -> NumberUtil.format(this.getDurabilityAmount(level)));
}
@Override
public boolean isCurse() {
return true;
}
@NotNull
@Override
public ChanceImplementation getChanceImplementation() {
return chanceImplementation;
}
public int getDurabilityAmount(int level) {
return (int) this.durabilityAmount.getValue(level);
}
@NotNull
@Override
public EnchantmentTarget getCategory() {
return EnchantmentTarget.BREAKABLE;
}
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
public void onItemDurability(PlayerItemDamageEvent event) {
Player player = event.getPlayer();
if (!this.isAvailableToUse(player)) return;
ItemStack item = event.getItem();
int level = EnchantUtils.getLevel(item, this.getBackend());
if (level < 1) return;
if (!this.checkTriggerChance(level)) return;
int durabilityAmount = this.getDurabilityAmount(level);
if (durabilityAmount <= 0) return;
event.setDamage(event.getDamage() + durabilityAmount);
}
}

View File

@ -0,0 +1,110 @@
package su.nightexpress.excellentenchants.enchantment.impl.tool;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventPriority;
import org.bukkit.event.block.BlockDropItemEvent;
import org.bukkit.event.entity.EntityDeathEvent;
import org.bukkit.event.entity.EntityResurrectEvent;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import su.nexmedia.engine.utils.ItemUtil;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.api.enchantment.meta.Chanced;
import su.nightexpress.excellentenchants.api.enchantment.type.BlockDropEnchant;
import su.nightexpress.excellentenchants.api.enchantment.type.DeathEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.meta.ChanceImplementation;
import su.nightexpress.excellentenchants.api.enchantment.ItemCategory;
public class CurseOfMediocrityEnchant extends ExcellentEnchant implements Chanced, BlockDropEnchant, DeathEnchant {
public static final String ID = "curse_of_mediocrity";
private ChanceImplementation chanceImplementation;
public CurseOfMediocrityEnchant(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription(Placeholders.ENCHANTMENT_CHANCE + "% chance to disenchant item drops.");
this.getDefaults().setLevelMax(3);
this.getDefaults().setTier(0D);
}
@Override
public void loadSettings() {
super.loadSettings();
this.chanceImplementation = ChanceImplementation.create(this, "25.0 * " + Placeholders.ENCHANTMENT_LEVEL);
}
@NotNull
@Override
public EnchantmentTarget getCategory() {
return EnchantmentTarget.BREAKABLE;
}
@Override
@NotNull
public ItemCategory[] getFitItemTypes() {
return new ItemCategory[] {
ItemCategory.SWORD, ItemCategory.BOW, ItemCategory.CROSSBOW, ItemCategory.TRIDENT, ItemCategory.TOOL
};
}
@NotNull
@Override
public EventPriority getDropPriority() {
return EventPriority.HIGHEST;
}
@NotNull
@Override
public Chanced getChanceImplementation() {
return this.chanceImplementation;
}
@Override
public boolean isCurse() {
return true;
}
@Override
public boolean onDrop(@NotNull BlockDropItemEvent event,
@NotNull LivingEntity player, @NotNull ItemStack item, int level) {
if (!this.checkTriggerChance(level)) return false;
event.getItems().forEach(drop -> {
ItemStack stack = drop.getItemStack();
ItemUtil.mapMeta(stack, meta -> {
meta.getEnchants().keySet().forEach(meta::removeEnchant);
});
drop.setItemStack(stack);
});
return true;
}
@Override
public boolean onDeath(@NotNull EntityDeathEvent event, @NotNull LivingEntity entity, ItemStack item, int level) {
return false;
}
@Override
public boolean onResurrect(@NotNull EntityResurrectEvent event, @NotNull LivingEntity entity, @NotNull ItemStack item, int level) {
return false;
}
@Override
public boolean onKill(@NotNull EntityDeathEvent event, @NotNull LivingEntity entity, @NotNull Player killer, ItemStack weapon, int level) {
if (!this.checkTriggerChance(level)) return false;
event.getDrops().forEach(stack -> {
ItemUtil.mapMeta(stack, meta -> {
meta.getEnchants().keySet().forEach(meta::removeEnchant);
});
});
return true;
}
}

View File

@ -0,0 +1,119 @@
package su.nightexpress.excellentenchants.enchantment.impl.tool;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.Item;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventPriority;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.entity.EntityDeathEvent;
import org.bukkit.event.entity.EntityResurrectEvent;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import su.nexmedia.engine.api.config.JOption;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.api.enchantment.meta.Chanced;
import su.nightexpress.excellentenchants.api.enchantment.type.BlockBreakEnchant;
import su.nightexpress.excellentenchants.api.enchantment.type.DeathEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.meta.ChanceImplementation;
import su.nightexpress.excellentenchants.api.enchantment.ItemCategory;
public class CurseOfMisfortuneEnchant extends ExcellentEnchant implements Chanced, BlockBreakEnchant, DeathEnchant {
public static final String ID = "curse_of_misfortune";
private boolean dropExp;
private ChanceImplementation chanceImplementation;
public CurseOfMisfortuneEnchant(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription(Placeholders.ENCHANTMENT_CHANCE + "% chance to have no drops from blocks or mobs.");
this.getDefaults().setLevelMax(3);
this.getDefaults().setTier(0D);
this.getDefaults().setConflicts(
Enchantment.LOOT_BONUS_BLOCKS.getKey().getKey(),
Enchantment.LOOT_BONUS_MOBS.getKey().getKey()
);
}
@Override
public void loadSettings() {
super.loadSettings();
this.chanceImplementation = ChanceImplementation.create(this,
"20.0 * " + Placeholders.ENCHANTMENT_LEVEL);
this.dropExp = JOption.create("Settings.Drop_Exp", false,
"When 'true' allows to drop exp from mobs/blocks.").read(cfg);
}
@NotNull
@Override
public ChanceImplementation getChanceImplementation() {
return chanceImplementation;
}
public boolean isDropExp() {
return dropExp;
}
@Override
@NotNull
public ItemCategory[] getFitItemTypes() {
return new ItemCategory[] {
ItemCategory.SWORD, ItemCategory.BOW, ItemCategory.CROSSBOW, ItemCategory.TRIDENT, ItemCategory.TOOL
};
}
@NotNull
@Override
public EnchantmentTarget getCategory() {
return EnchantmentTarget.BREAKABLE;
}
@NotNull
@Override
public EventPriority getBreakPriority() {
return EventPriority.HIGHEST;
}
@NotNull
@Override
public EventPriority getKillPriority() {
return EventPriority.HIGH;
}
@Override
public boolean isCurse() {
return true;
}
@Override
public boolean onBreak(@NotNull BlockBreakEvent event, @NotNull LivingEntity player, @NotNull ItemStack item, int level) {
if (!this.checkTriggerChance(level)) return false;
event.setDropItems(false);
if (!this.isDropExp()) event.setExpToDrop(0);
return true;
}
@Override
public boolean onKill(@NotNull EntityDeathEvent event, @NotNull LivingEntity entity, @NotNull Player killer, ItemStack weapon, int level) {
if (!this.checkTriggerChance(level)) return false;
event.getDrops().clear();
if (!this.isDropExp()) event.setDroppedExp(0);
return true;
}
@Override
public boolean onDeath(@NotNull EntityDeathEvent event, @NotNull LivingEntity entity, ItemStack item, int level) {
return false;
}
@Override
public boolean onResurrect(@NotNull EntityResurrectEvent event, @NotNull LivingEntity entity, @NotNull ItemStack item, int level) {
return false;
}
}

View File

@ -0,0 +1,168 @@
package su.nightexpress.excellentenchants.enchantment.impl.tool;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.Particle;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.block.CreatureSpawner;
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.block.BlockDropItemEvent;
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.JOption;
import su.nexmedia.engine.api.manager.EventListener;
import su.nexmedia.engine.lang.LangManager;
import su.nexmedia.engine.utils.Colorizer;
import su.nexmedia.engine.utils.Colors;
import su.nexmedia.engine.utils.LocationUtil;
import su.nexmedia.engine.utils.PDCUtil;
import su.nexmedia.engine.utils.values.UniParticle;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.api.enchantment.meta.Chanced;
import su.nightexpress.excellentenchants.api.enchantment.type.BlockBreakEnchant;
import su.nightexpress.excellentenchants.api.enchantment.type.BlockDropEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.meta.ChanceImplementation;
import su.nightexpress.excellentenchants.api.enchantment.ItemCategory;
import su.nightexpress.excellentenchants.enchantment.util.EnchantUtils;
public class DivineTouchEnchant extends ExcellentEnchant implements Chanced, BlockBreakEnchant, BlockDropEnchant, EventListener {
public static final String ID = "divine_touch";
private final NamespacedKey key;
private String spawnerName;
private ChanceImplementation chanceImplementation;
private Location handleSpawner;
public DivineTouchEnchant(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.key = new NamespacedKey(plugin, "divine_spawner");
this.getDefaults().setDescription(Placeholders.ENCHANTMENT_CHANCE + "% chance to mine spawner.");
this.getDefaults().setLevelMax(5);
this.getDefaults().setTier(1.0);
this.getDefaults().setConflicts(SmelterEnchant.ID);
}
@Override
public void loadSettings() {
super.loadSettings();
this.chanceImplementation = ChanceImplementation.create(this,
"15.0 * " + Placeholders.ENCHANTMENT_LEVEL);
this.spawnerName = JOption.create("Settings.Spawner_Item.Name",
Colors.GREEN + "Mob Spawner " + Colors.GRAY + "(" + Placeholders.GENERIC_TYPE + ")",
"Spawner item display name.",
"Placeholder '" + Placeholders.GENERIC_TYPE + "' for the mob type."
).mapReader(Colorizer::apply).read(cfg);
}
@NotNull
@Override
public ChanceImplementation getChanceImplementation() {
return chanceImplementation;
}
@Override
@NotNull
public ItemCategory[] getFitItemTypes() {
return new ItemCategory[]{ItemCategory.PICKAXE};
}
@Override
@NotNull
public EnchantmentTarget getCategory() {
return EnchantmentTarget.TOOL;
}
@NotNull
@Override
public EventPriority getDropPriority() {
return EventPriority.NORMAL;
}
@NotNull
@Override
public EventPriority getBreakPriority() {
return EventPriority.HIGH;
}
@NotNull
public ItemStack getSpawner(@NotNull CreatureSpawner spawnerBlock) {
ItemStack itemSpawner = new ItemStack(Material.SPAWNER);
BlockStateMeta stateItem = (BlockStateMeta) itemSpawner.getItemMeta();
if (stateItem == null || spawnerBlock.getSpawnedType() == null) return itemSpawner;
CreatureSpawner spawnerItem = (CreatureSpawner) stateItem.getBlockState();
spawnerItem.setSpawnedType(spawnerBlock.getSpawnedType());
spawnerItem.update(true);
stateItem.setBlockState(spawnerItem);
stateItem.setDisplayName(this.spawnerName.replace(Placeholders.GENERIC_TYPE, LangManager.getEntityType(spawnerBlock.getSpawnedType())));
itemSpawner.setItemMeta(stateItem);
PDCUtil.set(itemSpawner, this.key, true);
return itemSpawner;
}
@Override
public boolean onDrop(@NotNull BlockDropItemEvent event, @NotNull LivingEntity player, @NotNull ItemStack item, int level) {
BlockState state = event.getBlockState();
Block block = state.getBlock();
if (this.handleSpawner == null || !this.handleSpawner.equals(block.getLocation())) return false;
this.handleSpawner = null;
if (!(state instanceof CreatureSpawner spawnerBlock)) return false;
EnchantUtils.popResource(event, this.getSpawner(spawnerBlock));
if (this.hasVisualEffects()) {
Location location = LocationUtil.getCenter(block.getLocation());
UniParticle.of(Particle.VILLAGER_HAPPY).play(location, 0.3, 0.15, 30);
}
return true;
}
@Override
public boolean onBreak(@NotNull BlockBreakEvent event, @NotNull LivingEntity player, @NotNull ItemStack item, int level) {
Block block = event.getBlock();
if (!(block.getState() instanceof CreatureSpawner spawnerBlock)) return false;
if (!this.checkTriggerChance(level)) return false;
event.setExpToDrop(0);
event.setDropItems(true);
this.handleSpawner = block.getLocation();
return false; // Do not consume charges
}
// 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().getItem(e.getHand());
if (spawner == null || spawner.getType() != Material.SPAWNER || !(spawner.getItemMeta() instanceof BlockStateMeta meta)) return;
if (PDCUtil.getBoolean(spawner, this.key).isEmpty()) return;
CreatureSpawner spawnerItem = (CreatureSpawner) meta.getBlockState();
CreatureSpawner spawnerBlock = (CreatureSpawner) block.getState();
spawnerBlock.setSpawnedType(spawnerItem.getSpawnedType());
spawnerBlock.update();
}
}

View File

@ -0,0 +1,59 @@
package su.nightexpress.excellentenchants.enchantment.impl.tool;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.LivingEntity;
import org.bukkit.inventory.ItemStack;
import org.bukkit.potion.PotionEffectType;
import org.jetbrains.annotations.NotNull;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.api.enchantment.meta.Potioned;
import su.nightexpress.excellentenchants.api.enchantment.type.PassiveEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.meta.PeriodImplementation;
import su.nightexpress.excellentenchants.enchantment.impl.meta.PotionImplementation;
public class HasteEnchant extends ExcellentEnchant implements Potioned, PassiveEnchant {
public static final String ID = "haste";
private PotionImplementation potionImplementation;
private PeriodImplementation periodImplementation;
public HasteEnchant(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription("Grants permanent " + Placeholders.ENCHANTMENT_POTION_TYPE + " " + Placeholders.ENCHANTMENT_POTION_LEVEL + " effect.");
this.getDefaults().setLevelMax(3);
this.getDefaults().setTier(0.3);
}
@Override
public void loadSettings() {
super.loadSettings();
this.potionImplementation = PotionImplementation.create(this, PotionEffectType.FAST_DIGGING, true);
this.periodImplementation = PeriodImplementation.create(this, "100");
}
@NotNull
@Override
public PotionImplementation getPotionImplementation() {
return potionImplementation;
}
@NotNull
@Override
public PeriodImplementation getPeriodImplementation() {
return periodImplementation;
}
@Override
@NotNull
public EnchantmentTarget getCategory() {
return EnchantmentTarget.TOOL;
}
@Override
public boolean onTrigger(@NotNull LivingEntity entity, @NotNull ItemStack item, int level) {
return this.addEffect(entity, level);
}
}

View File

@ -0,0 +1,75 @@
package su.nightexpress.excellentenchants.enchantment.impl.tool;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.LivingEntity;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import su.nexmedia.engine.utils.NumberUtil;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.api.enchantment.meta.Chanced;
import su.nightexpress.excellentenchants.api.enchantment.type.BlockBreakEnchant;
import su.nightexpress.excellentenchants.enchantment.config.EnchantScaler;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.meta.ChanceImplementation;
import su.nightexpress.excellentenchants.api.enchantment.ItemCategory;
public class LuckyMinerEnchant extends ExcellentEnchant implements Chanced, BlockBreakEnchant {
public static final String ID = "lucky_miner";
private static final String PLACEHOLDER_EXP_MODIFIER = "%enchantment_exp_modifier%";
private EnchantScaler expModifier;
private ChanceImplementation chanceImplementation;
public LuckyMinerEnchant(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription(Placeholders.ENCHANTMENT_CHANCE + "% chance to gain " + PLACEHOLDER_EXP_MODIFIER + "% more exp from ores.");
this.getDefaults().setLevelMax(5);
this.getDefaults().setTier(0.1);
}
@Override
public void loadSettings() {
super.loadSettings();
this.chanceImplementation = ChanceImplementation.create(this,
"30.0 + " + Placeholders.ENCHANTMENT_LEVEL + " * 7.0");
this.expModifier = EnchantScaler.read(this, "Settings.Exp_Modifier",
"1.0 + " + Placeholders.ENCHANTMENT_LEVEL + " * 0.5",
"Exp modifier value. The original exp amount will be multiplied on this value.");
this.addPlaceholder(PLACEHOLDER_EXP_MODIFIER, level -> NumberUtil.format(this.getExpModifier(level) * 100D - 100D));
}
@NotNull
@Override
public ChanceImplementation getChanceImplementation() {
return chanceImplementation;
}
public double getExpModifier(int level) {
return this.expModifier.getValue(level);
}
@Override
@NotNull
public ItemCategory[] getFitItemTypes() {
return new ItemCategory[]{ItemCategory.PICKAXE};
}
@Override
@NotNull
public EnchantmentTarget getCategory() {
return EnchantmentTarget.TOOL;
}
@Override
public boolean onBreak(@NotNull BlockBreakEvent event, @NotNull LivingEntity player, @NotNull ItemStack item, int level) {
if (!this.checkTriggerChance(level)) return false;
double expMod = this.getExpModifier(level);
event.setExpToDrop((int) ((double) event.getExpToDrop() * expMod));
return true;
}
}

View File

@ -0,0 +1,200 @@
package su.nightexpress.excellentenchants.enchantment.impl.tool;
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.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventPriority;
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.JOption;
import su.nexmedia.engine.utils.values.UniSound;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.api.enchantment.meta.Chanced;
import su.nightexpress.excellentenchants.api.enchantment.type.BlockBreakEnchant;
import su.nightexpress.excellentenchants.api.enchantment.type.InteractEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.meta.ChanceImplementation;
import su.nightexpress.excellentenchants.api.enchantment.ItemCategory;
import java.util.Set;
public class ReplanterEnchant extends ExcellentEnchant implements Chanced, InteractEnchant, BlockBreakEnchant {
public static final String ID = "replanter";
private boolean replantOnRightClick;
private boolean replantOnPlantBreak;
private ChanceImplementation chanceImplementation;
private static final Set<Material> CROPS = Set.of(
Material.WHEAT_SEEDS, Material.BEETROOT_SEEDS,
Material.MELON_SEEDS, Material.PUMPKIN_SEEDS,
Material.POTATO, Material.CARROT, Material.NETHER_WART);
public ReplanterEnchant(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription("Automatically replant crops on right click and when harvest.");
this.getDefaults().setLevelMax(1);
this.getDefaults().setTier(0.3);
}
@Override
public void loadSettings() {
super.loadSettings();
this.chanceImplementation = ChanceImplementation.create(this, "100");
this.replantOnRightClick = JOption.create("Settings.Replant.On_Right_Click", true,
"When 'true', player will be able to replant crops when right-clicking farmland blocks.").read(cfg);
this.replantOnPlantBreak = JOption.create("Settings.Replant.On_Plant_Break", true,
"When 'true', crops will be automatically replanted when player break plants with enchanted tool in hand.").read(cfg);
}
@NotNull
@Override
public ChanceImplementation getChanceImplementation() {
return chanceImplementation;
}
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
@NotNull
public ItemCategory[] getFitItemTypes() {
return new ItemCategory[]{ItemCategory.HOE};
}
@Override
@NotNull
public EnchantmentTarget getCategory() {
return EnchantmentTarget.TOOL;
}
@NotNull
@Override
public EventPriority getInteractPriority() {
return EventPriority.HIGHEST;
}
@Override
public boolean onInteract(@NotNull PlayerInteractEvent event, @NotNull LivingEntity entity, @NotNull ItemStack item, int level) {
if (!(entity instanceof Player player)) return false;
if (!this.isReplantOnRightClick()) return false;
if (!this.checkTriggerChance(level)) return false;
// Check for a event hand. We dont want to trigger it twice.
if (event.getHand() != EquipmentSlot.HAND) return false;
if (event.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 = event.getClickedBlock();
if (blockGround == null) return false;
if (blockGround.getType() != Material.FARMLAND && blockGround.getType() != Material.SOUL_SAND) 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)) {
UniSound.of(seed == Material.NETHER_WART ? Sound.ITEM_NETHER_WART_PLANT : Sound.ITEM_CROP_PLANT).play(player);
plugin.getEnchantNMS().sendAttackPacket(player, 0);
blockPlant.setType(this.fineSeedsToBlock(seed));
break;
}
}
}
return true;
}
@Override
public boolean onBreak(@NotNull BlockBreakEvent event, @NotNull LivingEntity entity, @NotNull ItemStack item, int level) {
if (!(entity instanceof Player player)) return false;
if (!this.isReplantOnPlantBreak()) return false;
if (!this.checkTriggerChance(level)) return false;
Block blockPlant = event.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 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,203 @@
package su.nightexpress.excellentenchants.enchantment.impl.tool;
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.Item;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.block.BlockDropItemEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.inventory.ClickType;
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.BlockStateMeta;
import org.jetbrains.annotations.NotNull;
import su.nexmedia.engine.api.config.JOption;
import su.nexmedia.engine.api.manager.EventListener;
import su.nexmedia.engine.utils.Colorizer;
import su.nexmedia.engine.utils.ItemUtil;
import su.nexmedia.engine.utils.PDCUtil;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.api.enchantment.type.BlockDropEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.api.enchantment.ItemCategory;
import su.nightexpress.excellentenchants.enchantment.util.EnchantUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
public class SilkChestEnchant extends ExcellentEnchant implements BlockDropEnchant, EventListener {
public static final String ID = "silk_chest";
private String chestName;
private List<String> chestLore;
private final NamespacedKey keyChest;
public SilkChestEnchant(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription("Drop chests and saves all its content.");
this.getDefaults().setLevelMax(1);
this.getDefaults().setTier(0.5);
this.keyChest = new NamespacedKey(plugin, ID + ".item");
}
@Override
public void loadSettings() {
super.loadSettings();
this.chestName = JOption.create("Settings.Chest_Item.Name", "Chest &7(" + Placeholders.GENERIC_AMOUNT + " items)",
"Chest item display name.",
"Use '" + Placeholders.GENERIC_AMOUNT + "' for items amount.").mapReader(Colorizer::apply).read(cfg);
this.chestLore = JOption.create("Settings.Chest_Item.Lore", new ArrayList<>(),
"Chest item lore.",
"Use '" + Placeholders.GENERIC_AMOUNT + "' for items amount.").mapReader(Colorizer::apply).read(cfg);
}
@Override
@NotNull
public ItemCategory[] getFitItemTypes() {
return new ItemCategory[]{ItemCategory.AXE};
}
@Override
@NotNull
public EnchantmentTarget getCategory() {
return EnchantmentTarget.TOOL;
}
@NotNull
@Override
public EventPriority getDropPriority() {
return EventPriority.NORMAL;
}
public boolean isSilkChest(@NotNull ItemStack item) {
return PDCUtil.getBoolean(item, this.keyChest).isPresent();
}
@NotNull
public ItemStack getSilkChest(@NotNull Chest chest) {
ItemStack chestStack = new ItemStack(chest.getType());
BlockStateMeta stateMeta = (BlockStateMeta) chestStack.getItemMeta();
if (stateMeta == null) return chestStack;
Chest chestItem = (Chest) stateMeta.getBlockState();
chestItem.getBlockInventory().setContents(chest.getBlockInventory().getContents());
chestItem.update(true);
int amount = (int) Stream.of(chestItem.getBlockInventory().getContents()).filter(i -> i != null && !i.getType().isAir()).count();
stateMeta.setBlockState(chestItem);
stateMeta.setDisplayName(this.chestName);
stateMeta.setLore(this.chestLore);
chestStack.setItemMeta(stateMeta);
ItemUtil.replace(chestStack, str -> str.replace(Placeholders.GENERIC_AMOUNT, String.valueOf(amount)));
PDCUtil.set(chestStack, this.keyChest, true);
return chestStack;
// 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) {
chest.getWorld().dropItemNaturally(chest.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
public boolean onDrop(@NotNull BlockDropItemEvent event,
@NotNull LivingEntity player, @NotNull ItemStack item, int level) {
BlockState state = event.getBlockState();
Block block = state.getBlock();
if (!(state instanceof Chest chest)) return false;
// Добавляем в сундук обратно предметы из дроп листа, кроме самого сундука.
event.getItems().removeIf(drop -> drop.getItemStack().getType() == state.getType() && drop.getItemStack().getAmount() == 1);
chest.getBlockInventory().addItem(event.getItems().stream().map(Item::getItemStack).toList().toArray(new ItemStack[0]));
event.getItems().clear();
if (chest.getBlockInventory().isEmpty()) {
EnchantUtils.popResource(event, new ItemStack(chest.getType()));
return false;
}
EnchantUtils.popResource(event, this.getSilkChest(chest));
chest.getBlockInventory().clear();
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;
chest.setCustomName(null);
chest.update(true);
}
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onSilkChestStore(InventoryClickEvent e) {
Inventory inventory = e.getInventory();
if (inventory.getType() == InventoryType.CRAFTING || inventory.getType() == InventoryType.CREATIVE) return;
Player player = (Player) e.getWhoClicked();
ItemStack item;
if (e.getHotbarButton() >= 0) {
item = player.getInventory().getItem(e.getHotbarButton());
}
else item = e.getCurrentItem();
if (item == null || item.getType().isAir() || !this.isSilkChest(item)) return;
Inventory clicked = e.getClickedInventory();
if (e.getClick() != ClickType.NUMBER_KEY) {
if (clicked != null && clicked.equals(e.getView().getTopInventory())) return;
}
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,126 @@
package su.nightexpress.excellentenchants.enchantment.impl.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.block.Container;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.LivingEntity;
import org.bukkit.event.EventPriority;
import org.bukkit.event.block.BlockDropItemEvent;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import su.nexmedia.engine.api.config.JOption;
import su.nexmedia.engine.utils.LocationUtil;
import su.nexmedia.engine.utils.values.UniParticle;
import su.nexmedia.engine.utils.values.UniSound;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.api.enchantment.meta.Chanced;
import su.nightexpress.excellentenchants.api.enchantment.type.BlockDropEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.meta.ChanceImplementation;
import su.nightexpress.excellentenchants.api.enchantment.ItemCategory;
import java.util.Map;
public class SmelterEnchant extends ExcellentEnchant implements Chanced, BlockDropEnchant {
public static final String ID = "smelter";
private UniSound sound;
private Map<Material, Material> smeltingTable;
private ChanceImplementation chanceImplementation;
public SmelterEnchant(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription(Placeholders.ENCHANTMENT_CHANCE + "% chance to smelt a block/ore.");
this.getDefaults().setLevelMax(5);
this.getDefaults().setTier(0.3);
this.getDefaults().setConflicts(
DivineTouchEnchant.ID,
Enchantment.SILK_TOUCH.getKey().getKey()
);
}
@Override
public void loadSettings() {
super.loadSettings();
this.chanceImplementation = ChanceImplementation.create(this,
"25.0 + " + Placeholders.ENCHANTMENT_LEVEL + " * 10");
this.sound = JOption.create("Settings.Sound",UniSound.of(Sound.BLOCK_LAVA_EXTINGUISH),
"Sound to play on smelting.",
"https://hub.spigotmc.org/javadocs/bukkit/org/bukkit/Sound.html").read(cfg);
this.smeltingTable = JOption.forMap("Settings.Smelting_Table",
key -> Material.getMaterial(key.toUpperCase()),
(cfg, path, key) -> Material.getMaterial(cfg.getString(path + "." + key, "").toUpperCase()),
Map.of(
Material.RAW_IRON, Material.IRON_INGOT,
Material.RAW_GOLD, Material.GOLD_INGOT
),
"Table of Original -> Smelted items.",
"Syntax: 'Material Source : Material Result'.",
"Note: Material source is material name of the dropped item, not the broken block!",
"https://hub.spigotmc.org/javadocs/bukkit/org/bukkit/Material.html"
).setWriter((cfg, path, map) -> map.forEach((src, to) -> cfg.set(path + "." + src.name(), to.name()))).read(cfg);
}
@NotNull
@Override
public ChanceImplementation getChanceImplementation() {
return chanceImplementation;
}
@Override
@NotNull
public ItemCategory[] getFitItemTypes() {
return new ItemCategory[]{ItemCategory.PICKAXE, ItemCategory.AXE, ItemCategory.SHOVEL};
}
@Override
@NotNull
public EnchantmentTarget getCategory() {
return EnchantmentTarget.TOOL;
}
@NotNull
@Override
public EventPriority getDropPriority() {
return EventPriority.NORMAL;
}
@Override
public boolean onDrop(@NotNull BlockDropItemEvent event, @NotNull LivingEntity player, @NotNull ItemStack item, int level) {
// TODO Use furnace recipes & Re-add smelted items instead of setType
if (event.getBlockState() instanceof Container) return false;
if (!this.checkTriggerChance(level)) return false;
if (event.getItems().stream().noneMatch(drop -> this.isSmeltable(drop.getItemStack().getType()))) return false;
event.getItems().forEach(drop -> {
Material material = this.smeltingTable.get(drop.getItemStack().getType());
if (material != null) {
ItemStack stack = drop.getItemStack();
stack.setType(material);
drop.setItemStack(stack);
}
});
Block block = event.getBlockState().getBlock();
if (this.hasVisualEffects()) {
Location location = LocationUtil.getCenter(block.getLocation(), true);
UniParticle.of(Particle.FLAME).play(location, 0.25, 0.05, 20);
this.sound.play(location);
}
return true;
}
public boolean isSmeltable(@NotNull Material material) {
return this.smeltingTable.containsKey(material);
}
}

View File

@ -0,0 +1,72 @@
package su.nightexpress.excellentenchants.enchantment.impl.tool;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventPriority;
import org.bukkit.event.block.BlockDropItemEvent;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import su.nexmedia.engine.utils.PlayerUtil;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.api.enchantment.meta.Chanced;
import su.nightexpress.excellentenchants.api.enchantment.type.BlockDropEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.meta.ChanceImplementation;
import su.nightexpress.excellentenchants.api.enchantment.ItemCategory;
public class TelekinesisEnchant extends ExcellentEnchant implements Chanced, BlockDropEnchant {
public static final String ID = "telekinesis";
private ChanceImplementation chanceImplementation;
public TelekinesisEnchant(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription("Moves all blocks loot directly to your inventory.");
this.getDefaults().setLevelMax(1);
this.getDefaults().setTier(0.75);
}
@Override
public void loadSettings() {
super.loadSettings();
this.chanceImplementation = ChanceImplementation.create(this, "100");
}
@NotNull
@Override
public ChanceImplementation getChanceImplementation() {
return chanceImplementation;
}
@Override
@NotNull
public ItemCategory[] getFitItemTypes() {
return new ItemCategory[]{ItemCategory.TOOL};
}
@Override
@NotNull
public EnchantmentTarget getCategory() {
return EnchantmentTarget.TOOL;
}
@NotNull
@Override
public EventPriority getDropPriority() {
return EventPriority.HIGHEST;
}
@Override
public boolean onDrop(@NotNull BlockDropItemEvent event, @NotNull LivingEntity entity, @NotNull ItemStack item, int level) {
if (!(entity instanceof Player player)) return false;
if (!this.checkTriggerChance(level)) return false;
event.getItems().forEach(drop -> {
PlayerUtil.addItem(player, drop.getItemStack());
});
event.getItems().clear();
return true;
}
}

View File

@ -0,0 +1,167 @@
package su.nightexpress.excellentenchants.enchantment.impl.tool;
import org.bukkit.Material;
import org.bukkit.Tag;
import org.bukkit.block.Block;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.LivingEntity;
import org.bukkit.event.EventPriority;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.event.block.BlockDropItemEvent;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import su.nexmedia.engine.utils.blocktracker.PlayerBlockTracker;
import su.nexmedia.engine.utils.random.Rnd;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.api.enchantment.Cleanable;
import su.nightexpress.excellentenchants.api.enchantment.meta.Chanced;
import su.nightexpress.excellentenchants.api.enchantment.type.BlockBreakEnchant;
import su.nightexpress.excellentenchants.api.enchantment.type.BlockDropEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.meta.ChanceImplementation;
import su.nightexpress.excellentenchants.api.enchantment.ItemCategory;
import su.nightexpress.excellentenchants.enchantment.util.EnchantUtils;
import java.util.*;
import java.util.function.Predicate;
public class TreasuresEnchant extends ExcellentEnchant implements Chanced, BlockBreakEnchant, BlockDropEnchant, Cleanable {
public static final String ID = "treasures";
private final Predicate<Block> blockTracker;
private Map<Material, Map<Material, Double>> treasures;
private ChanceImplementation chanceImplementation;
private Block handleDrop;
public TreasuresEnchant(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription(Placeholders.ENCHANTMENT_CHANCE + "% chance to attempt to find a treasure in mined block.");
this.getDefaults().setLevelMax(5);
this.getDefaults().setTier(0.1);
PlayerBlockTracker.initialize();
PlayerBlockTracker.BLOCK_FILTERS.add(this.blockTracker = (block) -> {
return this.treasures.containsKey(block.getType());
});
}
@Override
public void loadSettings() {
super.loadSettings();
this.chanceImplementation = ChanceImplementation.create(this,
"10.0 + " + Placeholders.ENCHANTMENT_LEVEL + " * 4.0");
this.treasures = new HashMap<>();
if (cfg.getSection("Settings.Treasures").isEmpty()) {
Tag.BASE_STONE_OVERWORLD.getValues().forEach(material -> {
cfg.addMissing("Settings.Treasures." + material.name() + ".BONE_MEAL", 2.0);
});
Tag.DIRT.getValues().forEach(material -> {
cfg.addMissing("Settings.Treasures." + material.name() + ".CLAY_BALL", 0.5);
cfg.addMissing("Settings.Treasures." + material.name() + ".BOWL", 1.0);
cfg.addMissing("Settings.Treasures." + material.name() + ".STICK", 2.0);
});
Tag.SAND.getValues().forEach(material -> {
cfg.addMissing("Settings.Treasures." + material.name() + ".GLOWSTONE_DUST", 1.0);
cfg.addMissing("Settings.Treasures." + material.name() + ".GOLD_NUGGET", 0.3);
});
Tag.LEAVES.getValues().forEach(material -> {
cfg.addMissing("Settings.Treasures." + material.name() + ".APPLE", 12.0);
});
}
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);
}
}
this.cfg.setComments("Settings.Treasures",
"List of source materials (blocks that will drop additional loot). Separated by a comma.",
"https://hub.spigotmc.org/javadocs/bukkit/org/bukkit/Material.html");
}
@Override
public void clear() {
PlayerBlockTracker.BLOCK_FILTERS.remove(this.blockTracker);
}
@NotNull
@Override
public ChanceImplementation getChanceImplementation() {
return chanceImplementation;
}
@Override
@NotNull
public ItemCategory[] getFitItemTypes() {
return new ItemCategory[]{ItemCategory.PICKAXE, ItemCategory.AXE, ItemCategory.SHOVEL};
}
@Override
@NotNull
public EnchantmentTarget getCategory() {
return EnchantmentTarget.TOOL;
}
@NotNull
@Override
public EventPriority getDropPriority() {
return EventPriority.NORMAL;
}
@Override
public boolean onBreak(@NotNull BlockBreakEvent event, @NotNull LivingEntity player, @NotNull ItemStack item, int level) {
if (!event.isDropItems()) return false;
if (PlayerBlockTracker.isTracked(event.getBlock())) return false;
this.handleDrop = event.getBlock();
return false;
}
@Override
public boolean onDrop(@NotNull BlockDropItemEvent event, @NotNull LivingEntity player, @NotNull ItemStack item, int level) {
if (this.handleDrop != event.getBlock()) return false;
this.handleDrop = null;
if (!this.checkTriggerChance(level)) return false;
this.getTreasures(event.getBlockState().getType()).forEach(treasure -> {
EnchantUtils.popResource(event, treasure);
});
return true;
}
@NotNull
public final List<ItemStack> getTreasures(@NotNull Material type) {
List<ItemStack> list = new ArrayList<>();
Map<Material, Double> treasures = this.treasures.getOrDefault(type, Collections.emptyMap());
treasures.forEach((mat, chance) -> {
if (mat.isAir() || !mat.isItem() || !Rnd.chance(chance)) return;
list.add(new ItemStack(mat));
});
return list;
}
}

View File

@ -0,0 +1,125 @@
package su.nightexpress.excellentenchants.enchantment.impl.tool;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.LivingEntity;
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.JOption;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.api.enchantment.type.BlockBreakEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.api.enchantment.ItemCategory;
import su.nightexpress.excellentenchants.enchantment.util.EnchantUtils;
import su.nightexpress.excellentenchants.hook.impl.NoCheatPlusHook;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class TunnelEnchant extends ExcellentEnchant implements BlockBreakEnchant {
public static final String ID = "tunnel";
// 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);
INTERACTABLE_BLOCKS.add(Material.DEEPSLATE_REDSTONE_ORE);
}
private boolean disableOnSneak;
public TunnelEnchant(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription("Mines multiple blocks at once in a certain shape.");
this.getDefaults().setLevelMax(3);
this.getDefaults().setTier(1.0);
this.getDefaults().setConflicts(VeinminerEnchant.ID, BlastMiningEnchant.ID);
}
@Override
public void loadSettings() {
super.loadSettings();
this.disableOnSneak = JOption.create("Settings.Ignore_When_Sneaking", true,
"When 'true' the enchantment won't be triggered when sneaking.").read(cfg);
}
@Override
@NotNull
public ItemCategory[] getFitItemTypes() {
return new ItemCategory[]{ItemCategory.PICKAXE, ItemCategory.SHOVEL};
}
@Override
@NotNull
public EnchantmentTarget getCategory() {
return EnchantmentTarget.TOOL;
}
@Override
public boolean onBreak(@NotNull BlockBreakEvent event, @NotNull LivingEntity entity, @NotNull ItemStack item, int level) {
if (!(entity instanceof Player player)) return false;
if (EnchantUtils.isBusy()) return false;
if (this.disableOnSneak && player.isSneaking()) return false;
Block block = event.getBlock();
if (block.getType().isInteractable() && !INTERACTABLE_BLOCKS.contains(block.getType())) return false;
if (block.getDrops(item).isEmpty()) return false;
final List<Block> lastTwoTargetBlocks = player.getLastTwoTargetBlocks(null, 10);
if (lastTwoTargetBlocks.size() != 2 || !lastTwoTargetBlocks.get(1).getType().isOccluding()) {
return false;
}
final Block targetBlock = lastTwoTargetBlocks.get(1);
final Block adjacentBlock = lastTwoTargetBlocks.get(0);
final BlockFace dir = targetBlock.getFace(adjacentBlock);
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;
NoCheatPlusHook.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 (dir == BlockFace.UP || dir == BlockFace.DOWN) {
blockAdd = block.getLocation().clone().add(xAdd, 0, zAdd).getBlock();
} else {
blockAdd = block.getLocation().clone().add(isZ ? 0 : xAdd, zAdd, isZ ? xAdd : 0).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;
EnchantUtils.safeBusyBreak(player, blockAdd);
}
NoCheatPlusHook.unexemptBlocks(player);
return true;
}
}

View File

@ -0,0 +1,140 @@
package su.nightexpress.excellentenchants.enchantment.impl.tool;
import org.bukkit.Material;
import org.bukkit.Tag;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.LivingEntity;
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.JOption;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.api.enchantment.type.BlockBreakEnchant;
import su.nightexpress.excellentenchants.enchantment.config.EnchantScaler;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.api.enchantment.ItemCategory;
import su.nightexpress.excellentenchants.enchantment.util.EnchantUtils;
import su.nightexpress.excellentenchants.hook.impl.NoCheatPlusHook;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class VeinminerEnchant extends ExcellentEnchant implements BlockBreakEnchant {
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 PLACEHOLDER_BLOCK_LIMIT = "%enchantment_block_limit%";
private EnchantScaler blocksLimit;
private Set<Material> blocksAffected;
public VeinminerEnchant(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription("Mines up to " + PLACEHOLDER_BLOCK_LIMIT + " blocks of the ore vein at once.");
this.getDefaults().setLevelMax(3);
this.getDefaults().setTier(0.3);
this.getDefaults().setConflicts(BlastMiningEnchant.ID, TunnelEnchant.ID);
}
@Override
public void loadSettings() {
super.loadSettings();
this.blocksLimit = EnchantScaler.read(this, "Settings.Blocks.Max_At_Once",
"6 + " + Placeholders.ENCHANTMENT_LEVEL,
"How much amount of blocks can be destroted at single use?");
this.blocksAffected = JOption.forSet("Settings.Blocks.Affected",
str -> Material.getMaterial(str.toUpperCase()),
() -> {
Set<Material> set = new HashSet<>();
set.addAll(Tag.COAL_ORES.getValues());
set.addAll(Tag.COPPER_ORES.getValues());
set.addAll(Tag.DIAMOND_ORES.getValues());
set.addAll(Tag.EMERALD_ORES.getValues());
set.addAll(Tag.GOLD_ORES.getValues());
set.addAll(Tag.IRON_ORES.getValues());
set.addAll(Tag.LAPIS_ORES.getValues());
set.addAll(Tag.REDSTONE_ORES.getValues());
set.add(Material.NETHER_GOLD_ORE);
set.add(Material.NETHER_QUARTZ_ORE);
return set;
},
"List of blocks, that will be affected by this enchantment.",
"https://hub.spigotmc.org/javadocs/bukkit/org/bukkit/Material.html"
).setWriter((cfg, path, set) -> cfg.set(path, set.stream().map(Enum::name).toList())).read(cfg);
this.addPlaceholder(PLACEHOLDER_BLOCK_LIMIT, level -> String.valueOf(this.getBlocksLimit(level)));
}
@NotNull
public Set<Material> getBlocksAffected() {
return this.blocksAffected;
}
public int getBlocksLimit(int level) {
return (int) this.blocksLimit.getValue(level);
}
@Override
@NotNull
public ItemCategory[] getFitItemTypes() {
return new ItemCategory[]{ItemCategory.PICKAXE};
}
@NotNull
@Override
public EnchantmentTarget getCategory() {
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 -> EnchantUtils.safeBusyBreak(player, ore));
}
@Override
public boolean onBreak(@NotNull BlockBreakEvent event, @NotNull LivingEntity entity, @NotNull ItemStack tool, int level) {
if (!(entity instanceof Player player)) return false;
if (EnchantUtils.isBusy()) return false;
Block block = event.getBlock();
if (block.getDrops(tool, player).isEmpty()) return false;
if (!this.getBlocksAffected().contains(block.getType())) return false;
NoCheatPlusHook.exemptBlocks(player);
this.vein(player, block, level);
NoCheatPlusHook.unexemptBlocks(player);
return true;
}
}

View File

@ -0,0 +1,81 @@
package su.nightexpress.excellentenchants.enchantment.impl.universal;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
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.inventory.AnvilInventory;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import su.nexmedia.engine.api.manager.EventListener;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.api.enchantment.type.GenericEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.util.EnchantUtils;
public class CurseOfFragilityEnchant extends ExcellentEnchant implements GenericEnchant, EventListener {
public static final String ID = "curse_of_fragility";
public CurseOfFragilityEnchant(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription("Prevents an item from being grindstoned or anviled.");
this.getDefaults().setLevelMax(1);
this.getDefaults().setTier(0D);
}
@NotNull
@Override
public EnchantmentTarget getCategory() {
return EnchantmentTarget.BREAKABLE;
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onItemAnvil(PrepareAnvilEvent e) {
AnvilInventory inventory = e.getInventory();
ItemStack first = inventory.getItem(0);
ItemStack second = inventory.getItem(1);
boolean cursedFirst = (first != null && EnchantUtils.getLevel(first, this.getBackend()) >= 1);
boolean cursedSecond = (second != null && EnchantUtils.getLevel(second, this.getBackend()) >= 1);
if (cursedFirst || cursedSecond) {
e.setResult(null);
}
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onItemGrindstoneClick(InventoryClickEvent e) {
Inventory inventory = e.getInventory();
if (inventory.getType() != InventoryType.GRINDSTONE) return;
this.stopGrindstone(inventory);
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onItemGrindstoneDrag(InventoryDragEvent e) {
Inventory inventory = e.getInventory();
if (inventory.getType() != InventoryType.GRINDSTONE) return;
this.stopGrindstone(inventory);
}
private void stopGrindstone(@NotNull Inventory inventory) {
plugin.getScheduler().runTask(plugin, () -> {
ItemStack first = inventory.getItem(0);
ItemStack second = inventory.getItem(1);
boolean cursedFirst = (first != null && EnchantUtils.getLevel(first, this.getBackend()) >= 1);
boolean cursedSecond = (second != null && EnchantUtils.getLevel(second, this.getBackend()) >= 1);
if (cursedFirst || cursedSecond) {
inventory.setItem(2, null);
}
});
}
}

View File

@ -0,0 +1,97 @@
package su.nightexpress.excellentenchants.enchantment.impl.universal;
import org.bukkit.Sound;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.player.PlayerItemDamageEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.Damageable;
import org.jetbrains.annotations.NotNull;
import su.nexmedia.engine.api.manager.EventListener;
import su.nexmedia.engine.utils.NumberUtil;
import su.nexmedia.engine.utils.values.UniSound;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.api.enchantment.meta.Chanced;
import su.nightexpress.excellentenchants.api.enchantment.type.GenericEnchant;
import su.nightexpress.excellentenchants.enchantment.config.EnchantScaler;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.meta.ChanceImplementation;
import su.nightexpress.excellentenchants.enchantment.util.EnchantUtils;
public class RestoreEnchant extends ExcellentEnchant implements GenericEnchant, Chanced, EventListener {
public static final String ID = "restore";
public static final String PLACEHOLDER_DURABILITY_RESTORE = "%durability_restore%";
private ChanceImplementation chanceImplementation;
private EnchantScaler durabilityRestore;
public RestoreEnchant(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription(Placeholders.ENCHANTMENT_CHANCE + "% chance to save item from breaking back to " + PLACEHOLDER_DURABILITY_RESTORE + "%");
this.getDefaults().setLevelMax(5);
this.getDefaults().setTier(0.6);
}
@Override
public void loadSettings() {
super.loadSettings();
this.chanceImplementation = ChanceImplementation.create(this,
"35.0 + " + Placeholders.ENCHANTMENT_LEVEL + " * 8");
this.durabilityRestore = EnchantScaler.read(this, "Settings.Durability_Restoration",
"25.0 + " + Placeholders.ENCHANTMENT_LEVEL + " * 5",
"Amount of durability (in percent of item max) to be restored.");
this.addPlaceholder(PLACEHOLDER_DURABILITY_RESTORE, level -> NumberUtil.format(this.getDurabilityRestore(level)));
}
@NotNull
@Override
public ChanceImplementation getChanceImplementation() {
return chanceImplementation;
}
@NotNull
@Override
public EnchantmentTarget getCategory() {
return EnchantmentTarget.BREAKABLE;
}
public double getDurabilityRestore(int level) {
return this.durabilityRestore.getValue(level);
}
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
public void onItemDamage(PlayerItemDamageEvent event) {
ItemStack item = event.getItem();
if (!(item.getItemMeta() instanceof Damageable damageable)) return;
int damage = event.getDamage();
int maxDurability = item.getType().getMaxDurability();
if (damageable.getDamage() + damage < maxDurability) return;
int level = EnchantUtils.getLevel(item, this.getBackend());
if (level <= 0) return;
if (this.isOutOfCharges(item)) return;
if (!this.checkTriggerChance(level)) return;
event.setCancelled(true);
this.consumeChargesNoUpdate(item, level);
double restorePercent = 100D - this.getDurabilityRestore(level);
int restored = (int) (maxDurability * (restorePercent / 100D));
damageable.setDamage(restored);
item.setItemMeta(damageable);
EnchantUtils.remove(item, this.getBackend());
if (this.hasVisualEffects()) {
UniSound.of(Sound.ITEM_TOTEM_USE).play(event.getPlayer());
}
}
}

View File

@ -0,0 +1,73 @@
package su.nightexpress.excellentenchants.enchantment.impl.universal;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import su.nexmedia.engine.api.manager.EventListener;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.api.enchantment.type.GenericEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.util.EnchantUtils;
import java.util.ArrayList;
import java.util.List;
public class SoulboundEnchant extends ExcellentEnchant implements GenericEnchant, EventListener {
public static final String ID = "soulbound";
public SoulboundEnchant(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription("Protects from being dropped on death.");
this.getDefaults().setLevelMax(1);
this.getDefaults().setTier(0.8);
this.getDefaults().setConflicts(Enchantment.VANISHING_CURSE.getKey().getKey());
}
@NotNull
@Override
public EnchantmentTarget getCategory() {
return EnchantmentTarget.BREAKABLE;
}
@EventHandler
public void onDeath(@NotNull PlayerDeathEvent deathEvent) {
Player player = deathEvent.getEntity();
if (!this.isAvailableToUse(player)) return;
if (deathEvent.getKeepInventory()) return;
List<ItemStack> saveList = new ArrayList<>();
Location location = player.getLocation();
World world = player.getWorld();
deathEvent.getDrops().removeIf(drop -> {
if (EnchantUtils.getLevel(drop, this.getBackend()) > 0) {
if (this.isOutOfCharges(drop)) return false;
saveList.add(drop);
return true;
}
return false;
});
if (saveList.isEmpty()) return;
this.plugin.runTask(task -> {
saveList.forEach(save -> {
if (player.getInventory().firstEmpty() == -1) {
world.dropItemNaturally(location, save);
}
else {
this.consumeChargesNoUpdate(save, EnchantUtils.getLevel(save, this.getBackend()));
player.getInventory().addItem(save);
}
});
});
}
}

View File

@ -0,0 +1,72 @@
package su.nightexpress.excellentenchants.enchantment.impl.weapon;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.entity.EntityDeathEvent;
import org.bukkit.event.entity.EntityResurrectEvent;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.api.enchantment.meta.Chanced;
import su.nightexpress.excellentenchants.api.enchantment.type.DeathEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.meta.ChanceImplementation;
public class CurseOfDeathEnchant extends ExcellentEnchant implements DeathEnchant, Chanced {
public static final String ID = "curse_of_death";
private ChanceImplementation chanceImplementation;
public CurseOfDeathEnchant(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription("When killing players, you have a chance of dying too.");
this.getDefaults().setLevelMax(3);
this.getDefaults().setTier(0D);
}
@Override
public void loadSettings() {
super.loadSettings();
this.chanceImplementation = ChanceImplementation.create(this, Placeholders.ENCHANTMENT_LEVEL + " * 0.1");
}
@NotNull
@Override
public EnchantmentTarget getCategory() {
return EnchantmentTarget.WEAPON;
}
@NotNull
@Override
public Chanced getChanceImplementation() {
return this.chanceImplementation;
}
@Override
public boolean isCurse() {
return true;
}
@Override
public boolean onDeath(@NotNull EntityDeathEvent event, @NotNull LivingEntity entity, ItemStack item, int level) {
return false;
}
@Override
public boolean onResurrect(@NotNull EntityResurrectEvent event, @NotNull LivingEntity entity, @NotNull ItemStack item, int level) {
return false;
}
@Override
public boolean onKill(@NotNull EntityDeathEvent event, @NotNull LivingEntity entity, @NotNull Player killer, ItemStack weapon, int level) {
if (!(entity instanceof Player dead)) return false;
if (!this.checkTriggerChance(level)) return false;
killer.setHealth(0D);
return true;
}
}

View File

@ -0,0 +1,83 @@
package su.nightexpress.excellentenchants.enchantment.impl.weapon;
import org.bukkit.Particle;
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.JOption;
import su.nexmedia.engine.utils.NumberUtil;
import su.nexmedia.engine.utils.values.UniParticle;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.api.enchantment.type.CombatEnchant;
import su.nightexpress.excellentenchants.enchantment.config.EnchantScaler;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import java.util.Set;
public class EnchantBaneOfNetherspawn extends ExcellentEnchant implements CombatEnchant {
public static final String ID = "bane_of_netherspawn";
private static final String PLACEHOLDER_DAMAGE = "%enchantment_damage%";
private static final Set<EntityType> ENTITY_TYPES = Set.of(
EntityType.BLAZE, EntityType.MAGMA_CUBE,
EntityType.WITHER_SKELETON, EntityType.GHAST, EntityType.WITHER,
EntityType.PIGLIN, EntityType.PIGLIN_BRUTE,
EntityType.ZOGLIN, EntityType.HOGLIN,
EntityType.STRIDER, EntityType.ZOMBIFIED_PIGLIN
);
private boolean damageModifier;
private EnchantScaler damageFormula;
public EnchantBaneOfNetherspawn(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription("Inflicts " + PLACEHOLDER_DAMAGE + " more damage to nether mobs.");
this.getDefaults().setLevelMax(5);
this.getDefaults().setTier(0.1);
}
@Override
public void loadSettings() {
super.loadSettings();
this.damageModifier = JOption.create("Settings.Damage.As_Modifier", false,
"When 'true' multiplies the damage. When 'false' sums plain values.").read(cfg);
this.damageFormula = EnchantScaler.read(this, "Settings.Damage.Amount",
"0.5 * " + Placeholders.ENCHANTMENT_LEVEL,
"Amount of additional damage.");
this.addPlaceholder(PLACEHOLDER_DAMAGE, level -> NumberUtil.format(this.getDamageModifier(level)));
}
public double getDamageModifier(int level) {
return this.damageFormula.getValue(level);
}
@Override
@NotNull
public EnchantmentTarget getCategory() {
return EnchantmentTarget.WEAPON;
}
@Override
public boolean onAttack(@NotNull EntityDamageByEntityEvent event, @NotNull LivingEntity damager, @NotNull LivingEntity victim, @NotNull ItemStack weapon, int level) {
if (!ENTITY_TYPES.contains(victim.getType())) return false;
double damageEvent = event.getDamage();
double damageAdd = this.getDamageModifier(level);
event.setDamage(this.damageModifier ? damageEvent * damageAdd : damageEvent + damageAdd);
if (this.hasVisualEffects()) {
UniParticle.of(Particle.SMOKE_NORMAL).play(victim.getEyeLocation(), 0.25, 0.1, 30);
}
return true;
}
@Override
public boolean onProtect(@NotNull EntityDamageByEntityEvent event, @NotNull LivingEntity damager, @NotNull LivingEntity victim, @NotNull ItemStack weapon, int level) {
return false;
}
}

View File

@ -0,0 +1,84 @@
package su.nightexpress.excellentenchants.enchantment.impl.weapon;
import org.bukkit.Particle;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.LivingEntity;
import org.bukkit.event.EventPriority;
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.utils.values.UniParticle;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.api.enchantment.meta.Chanced;
import su.nightexpress.excellentenchants.api.enchantment.meta.Potioned;
import su.nightexpress.excellentenchants.api.enchantment.type.CombatEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.meta.ChanceImplementation;
import su.nightexpress.excellentenchants.enchantment.impl.meta.PotionImplementation;
public class EnchantBlindness extends ExcellentEnchant implements Chanced, Potioned, CombatEnchant {
public static final String ID = "blindness";
private ChanceImplementation chanceImplementation;
private PotionImplementation potionImplementation;
public EnchantBlindness(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription(Placeholders.ENCHANTMENT_CHANCE + "% chance to apply " + Placeholders.ENCHANTMENT_POTION_TYPE + " " + Placeholders.ENCHANTMENT_POTION_LEVEL + " (" + Placeholders.ENCHANTMENT_POTION_DURATION + "s.) on hit.");
this.getDefaults().setLevelMax(3);
this.getDefaults().setTier(0.1);
}
@Override
public void loadSettings() {
super.loadSettings();
this.chanceImplementation = ChanceImplementation.create(this,
"15.0 + " + Placeholders.ENCHANTMENT_LEVEL + " * 3");
this.potionImplementation = PotionImplementation.create(this, PotionEffectType.BLINDNESS, false,
"3.5 + " + Placeholders.ENCHANTMENT_LEVEL,
Placeholders.ENCHANTMENT_LEVEL);
}
@NotNull
@Override
public ChanceImplementation getChanceImplementation() {
return chanceImplementation;
}
@NotNull
@Override
public PotionImplementation getPotionImplementation() {
return potionImplementation;
}
@NotNull
@Override
public EnchantmentTarget getCategory() {
return EnchantmentTarget.WEAPON;
}
@NotNull
@Override
public EventPriority getAttackPriority() {
return EventPriority.HIGHEST;
}
@Override
public boolean onAttack(@NotNull EntityDamageByEntityEvent event, @NotNull LivingEntity damager, @NotNull LivingEntity victim, @NotNull ItemStack weapon, int level) {
if (!this.checkTriggerChance(level)) return false;
if (!this.addEffect(victim, level)) return false;
if (this.hasVisualEffects()) {
UniParticle.of(Particle.SMOKE_NORMAL).play(victim.getEyeLocation(), 0.25, 0.1, 30);
}
return true;
}
@Override
public boolean onProtect(@NotNull EntityDamageByEntityEvent event, @NotNull LivingEntity damager, @NotNull LivingEntity victim, @NotNull ItemStack weapon, int level) {
return false;
}
}

View File

@ -0,0 +1,84 @@
package su.nightexpress.excellentenchants.enchantment.impl.weapon;
import org.bukkit.Material;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.LivingEntity;
import org.bukkit.event.EventPriority;
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.utils.values.UniParticle;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.api.enchantment.meta.Chanced;
import su.nightexpress.excellentenchants.api.enchantment.meta.Potioned;
import su.nightexpress.excellentenchants.api.enchantment.type.CombatEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.meta.ChanceImplementation;
import su.nightexpress.excellentenchants.enchantment.impl.meta.PotionImplementation;
public class EnchantConfusion extends ExcellentEnchant implements Chanced, Potioned, CombatEnchant {
public static final String ID = "confusion";
private ChanceImplementation chanceImplementation;
private PotionImplementation potionImplementation;
public EnchantConfusion(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription(Placeholders.ENCHANTMENT_CHANCE + "% chance to apply " + Placeholders.ENCHANTMENT_POTION_TYPE + " " + Placeholders.ENCHANTMENT_POTION_LEVEL + " (" + Placeholders.ENCHANTMENT_POTION_DURATION + "s.) on hit.");
this.getDefaults().setLevelMax(3);
this.getDefaults().setTier(0.1);
}
@Override
public void loadSettings() {
super.loadSettings();
this.chanceImplementation = ChanceImplementation.create(this,
"15.0 * " + Placeholders.ENCHANTMENT_LEVEL);
this.potionImplementation = PotionImplementation.create(this, PotionEffectType.CONFUSION, false,
"5.0 + " + Placeholders.ENCHANTMENT_LEVEL + " * 1.5",
Placeholders.ENCHANTMENT_LEVEL);
}
@NotNull
@Override
public ChanceImplementation getChanceImplementation() {
return chanceImplementation;
}
@NotNull
@Override
public PotionImplementation getPotionImplementation() {
return potionImplementation;
}
@NotNull
@Override
public EnchantmentTarget getCategory() {
return EnchantmentTarget.WEAPON;
}
@NotNull
@Override
public EventPriority getAttackPriority() {
return EventPriority.HIGHEST;
}
@Override
public boolean onAttack(@NotNull EntityDamageByEntityEvent event, @NotNull LivingEntity damager, @NotNull LivingEntity victim, @NotNull ItemStack weapon, int level) {
if (!this.checkTriggerChance(level)) return false;
if (!this.addEffect(victim, level)) return false;
if (this.hasVisualEffects()) {
UniParticle.itemCrack(Material.ROTTEN_FLESH).play(victim.getEyeLocation(), 0.25, 0.1, 30);
}
return true;
}
@Override
public boolean onProtect(@NotNull EntityDamageByEntityEvent event, @NotNull LivingEntity damager, @NotNull LivingEntity victim, @NotNull ItemStack weapon, int level) {
return false;
}
}

View File

@ -0,0 +1,88 @@
package su.nightexpress.excellentenchants.enchantment.impl.weapon;
import com.google.common.collect.Sets;
import org.bukkit.Particle;
import org.bukkit.enchantments.EnchantmentTarget;
import org.bukkit.entity.*;
import org.bukkit.event.EventPriority;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import su.nexmedia.engine.utils.values.UniParticle;
import su.nightexpress.excellentenchants.ExcellentEnchants;
import su.nightexpress.excellentenchants.Placeholders;
import su.nightexpress.excellentenchants.api.enchantment.meta.Chanced;
import su.nightexpress.excellentenchants.api.enchantment.type.CombatEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.ExcellentEnchant;
import su.nightexpress.excellentenchants.enchantment.impl.meta.ChanceImplementation;
import java.util.Set;
public class EnchantCure extends ExcellentEnchant implements Chanced, CombatEnchant {
public static final String ID = "cure";
private ChanceImplementation chanceImplementation;
private static final Set<EntityType> CUREABLE = Sets.newHashSet(EntityType.ZOMBIFIED_PIGLIN, EntityType.ZOMBIE_VILLAGER);
public EnchantCure(@NotNull ExcellentEnchants plugin) {
super(plugin, ID);
this.getDefaults().setDescription(Placeholders.ENCHANTMENT_CHANCE + "% chance to cure Zombified Piglins and Zombie Villagers on hit.");
this.getDefaults().setLevelMax(5);
this.getDefaults().setTier(0.5);
}
@Override
public void loadSettings() {
super.loadSettings();
this.chanceImplementation = ChanceImplementation.create(this,
"20.0 + " + Placeholders.ENCHANTMENT_LEVEL + " * 8");
}
@NotNull
@Override
public ChanceImplementation getChanceImplementation() {
return chanceImplementation;
}
@Override
@NotNull
public EnchantmentTarget getCategory() {
return EnchantmentTarget.WEAPON;
}
@NotNull
@Override
public EventPriority getAttackPriority() {
return EventPriority.HIGHEST;
}
@Override
public boolean onAttack(@NotNull EntityDamageByEntityEvent event, @NotNull LivingEntity damager, @NotNull LivingEntity victim, @NotNull ItemStack weapon, int level) {
if (!CUREABLE.contains(victim.getType())) return false;
if (!this.checkTriggerChance(level)) return false;
if (!(damager instanceof Player player)) return false;
event.setCancelled(true);
if (this.hasVisualEffects()) {
UniParticle.of(Particle.CLOUD).play(victim.getEyeLocation(), 0.25, 0.1, 30);
}
if (victim instanceof PigZombie pigZombie) {
victim.getWorld().spawn(victim.getLocation(), Piglin.class);
victim.remove();
}
else if (victim instanceof ZombieVillager zombieVillager) {
zombieVillager.setConversionTime(1);
zombieVillager.setConversionPlayer(player);
}
return true;
}
@Override
public boolean onProtect(@NotNull EntityDamageByEntityEvent event, @NotNull LivingEntity damager, @NotNull LivingEntity victim, @NotNull ItemStack weapon, int level) {
return false;
}
}

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