add executioner enchant and refactor how default config keys are saved

This commit is contained in:
2024-10-25 14:24:15 +02:00
parent 18457a36ca
commit abddd8fb7d
6 changed files with 304 additions and 92 deletions
+6
View File
@@ -22,6 +22,12 @@ Replants broken crops using seeds in your inventory or from the loot of the crop
**Description**: **Description**:
Teleports dropped items to players location and makes them immediately pickuppable. Teleports dropped items to players location and makes them immediately pickuppable.
### Executioner
**Translation key**: `enchantio.enchantment.executioner`
**Description**:
Items enchanted with executioner will deal more damage to entities under specific health threshold.
## Configuration ## Configuration
@@ -1,5 +1,10 @@
package me.youhavetrouble.enchantio; package me.youhavetrouble.enchantio;
import me.youhavetrouble.enchantio.enchants.ExecutionerEnchant;
import me.youhavetrouble.enchantio.enchants.ReplantingEnchant;
import me.youhavetrouble.enchantio.enchants.SoulboundEnchant;
import me.youhavetrouble.enchantio.enchants.TelepathyEnchant;
import me.youhavetrouble.enchantio.listeners.ExecutionerListener;
import me.youhavetrouble.enchantio.listeners.ReplantingListener; import me.youhavetrouble.enchantio.listeners.ReplantingListener;
import me.youhavetrouble.enchantio.listeners.SoulboundListener; import me.youhavetrouble.enchantio.listeners.SoulboundListener;
import me.youhavetrouble.enchantio.listeners.TelepathyListener; import me.youhavetrouble.enchantio.listeners.TelepathyListener;
@@ -9,10 +14,19 @@ public final class Enchantio extends JavaPlugin {
@Override @Override
public void onEnable() { public void onEnable() {
if (EnchantioConfig.ENCHANTS.containsKey(SoulboundEnchant.KEY)) {
getServer().getPluginManager().registerEvents(new SoulboundListener(), this); getServer().getPluginManager().registerEvents(new SoulboundListener(), this);
}
if (EnchantioConfig.ENCHANTS.containsKey(TelepathyEnchant.KEY)) {
getServer().getPluginManager().registerEvents(new TelepathyListener(), this); getServer().getPluginManager().registerEvents(new TelepathyListener(), this);
}
if (EnchantioConfig.ENCHANTS.containsKey(ReplantingEnchant.KEY)) {
getServer().getPluginManager().registerEvents(new ReplantingListener(), this); getServer().getPluginManager().registerEvents(new ReplantingListener(), this);
} }
if (EnchantioConfig.ENCHANTS.containsKey(ExecutionerEnchant.KEY)) {
getServer().getPluginManager().registerEvents(new ExecutionerListener(), this);
}
}
@Override @Override
public void onDisable() { public void onDisable() {
@@ -12,9 +12,7 @@ import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.inventory.ItemType; import org.bukkit.inventory.ItemType;
import java.io.File; import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.*; import java.util.*;
import java.util.logging.Logger; import java.util.logging.Logger;
@@ -34,46 +32,32 @@ public class EnchantioConfig {
} }
File configFile = new File(filePath.toFile(), "config.yml"); File configFile = new File(filePath.toFile(), "config.yml");
if (!configFile.exists()) { configFile.createNewFile();
try (InputStream in = getClass().getResourceAsStream("/config.yml")) {
if (in == null) {
throw new IOException("Failed to load config.yml from resources");
}
try (FileOutputStream out = new FileOutputStream(configFile)) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
}
}
} catch (IOException e) {
throw new IOException("Failed saving default config", e);
}
}
FileConfiguration configuration = YamlConfiguration.loadConfiguration(configFile); FileConfiguration configuration = YamlConfiguration.loadConfiguration(configFile);
ConfigurationSection enchantsSection = configuration.getConfigurationSection("enchants"); ConfigurationSection enchantsSection = configuration.getConfigurationSection("enchants");
if (enchantsSection == null) { if (enchantsSection == null) {
throw new IOException("Failed to load enchants section from config"); enchantsSection = configuration.createSection("enchants");
} }
ConfigurationSection soulboundSection = enchantsSection.getConfigurationSection("soulbound"); ConfigurationSection soulboundSection = enchantsSection.getConfigurationSection("soulbound");
if (soulboundSection == null) { if (soulboundSection == null) {
soulboundSection = enchantsSection.createSection("soulbound"); soulboundSection = enchantsSection.createSection("soulbound");
} }
ENCHANTS.put(SoulboundEnchant.KEY, new SoulboundEnchant(
soulboundSection.getInt("anvilCost", 1), SoulboundEnchant soulboundEnchant = new SoulboundEnchant(
soulboundSection.getInt("weight", 10), getInt(soulboundSection, "anvilCost", 1),
getInt(soulboundSection, "weight", 10),
EnchantmentRegistryEntry.EnchantmentCost.of( EnchantmentRegistryEntry.EnchantmentCost.of(
soulboundSection.getInt("minimumCost.base", 10), getInt(soulboundSection, "minimumCost.base", 10),
soulboundSection.getInt("minimumCost.additionalPerLevel", 1) getInt(soulboundSection, "minimumCost.additionalPerLevel", 1)
), ),
EnchantmentRegistryEntry.EnchantmentCost.of( EnchantmentRegistryEntry.EnchantmentCost.of(
soulboundSection.getInt("maximumCost.base", 65), getInt(soulboundSection, "maximumCost.base", 65),
soulboundSection.getInt("maximumCost.additionalPerLevel", 1) getInt(soulboundSection, "maximumCost.additionalPerLevel", 1)
), ),
soulboundSection.getBoolean("canGetFromEnchantingTable", true), getBoolean(soulboundSection, "canGetFromEnchantingTable", true),
getTagsFromList(getStringList( getTagsFromList(getStringList(
soulboundSection, soulboundSection,
"supportedItemTags", "supportedItemTags",
@@ -83,24 +67,29 @@ public class EnchantioConfig {
"#minecraft:enchantable/mining" "#minecraft:enchantable/mining"
) )
)) ))
)); );
if (getBoolean(soulboundSection, "enabled", true)) {
ENCHANTS.put(SoulboundEnchant.KEY, soulboundEnchant);
}
ConfigurationSection telepathySection = enchantsSection.getConfigurationSection("telepathy"); ConfigurationSection telepathySection = enchantsSection.getConfigurationSection("telepathy");
if (telepathySection == null) { if (telepathySection == null) {
telepathySection = enchantsSection.createSection("telepathy"); telepathySection = enchantsSection.createSection("telepathy");
} }
ENCHANTS.put(TelepathyEnchant.KEY, new TelepathyEnchant(
telepathySection.getInt("anvilCost", 1), TelepathyEnchant telepathyEnchant = new TelepathyEnchant(
telepathySection.getInt("weight", 5), getInt(telepathySection,"anvilCost", 1),
getInt(telepathySection,"weight", 5),
EnchantmentRegistryEntry.EnchantmentCost.of( EnchantmentRegistryEntry.EnchantmentCost.of(
telepathySection.getInt("minimumCost.base", 15), getInt(telepathySection,"minimumCost.base", 15),
telepathySection.getInt("minimumCost.additionalPerLevel", 1) getInt(telepathySection,"minimumCost.additionalPerLevel", 1)
), ),
EnchantmentRegistryEntry.EnchantmentCost.of( EnchantmentRegistryEntry.EnchantmentCost.of(
telepathySection.getInt("maximumCost.base", 65), getInt(telepathySection,"maximumCost.base", 65),
telepathySection.getInt("maximumCost.additionalPerLevel", 1) getInt(telepathySection,"maximumCost.additionalPerLevel", 1)
), ),
telepathySection.getBoolean("canGetFromEnchantingTable", true), getBoolean(telepathySection, "canGetFromEnchantingTable", true),
getTagsFromList(getStringList( getTagsFromList(getStringList(
telepathySection, telepathySection,
"supportedItemTags", "supportedItemTags",
@@ -108,24 +97,29 @@ public class EnchantioConfig {
"#minecraft:enchantable/mining" "#minecraft:enchantable/mining"
) )
)) ))
)); );
if (getBoolean(telepathySection, "enabled", true)) {
ENCHANTS.put(TelepathyEnchant.KEY, telepathyEnchant);
}
ConfigurationSection replantingSection = enchantsSection.getConfigurationSection("replanting"); ConfigurationSection replantingSection = enchantsSection.getConfigurationSection("replanting");
if (replantingSection == null) { if (replantingSection == null) {
replantingSection = enchantsSection.createSection("replanting"); replantingSection = enchantsSection.createSection("replanting");
} }
ENCHANTS.put(ReplantingEnchant.KEY, new ReplantingEnchant(
replantingSection.getInt("anvilCost", 1), ReplantingEnchant replantingEnchant = new ReplantingEnchant(
replantingSection.getInt("weight", 10), getInt(replantingSection, "anvilCost", 1),
getInt(replantingSection, "weight", 10),
EnchantmentRegistryEntry.EnchantmentCost.of( EnchantmentRegistryEntry.EnchantmentCost.of(
replantingSection.getInt("minimumCost.base", 1), getInt(replantingSection, "minimumCost.base", 1),
replantingSection.getInt("minimumCost.additionalPerLevel", 1) getInt(replantingSection, "minimumCost.additionalPerLevel", 1)
), ),
EnchantmentRegistryEntry.EnchantmentCost.of( EnchantmentRegistryEntry.EnchantmentCost.of(
replantingSection.getInt("maximumCost.base", 65), getInt(replantingSection, "maximumCost.base", 65),
replantingSection.getInt("maximumCost.additionalPerLevel", 1) getInt(replantingSection, "maximumCost.additionalPerLevel", 1)
), ),
replantingSection.getBoolean("canGetFromEnchantingTable", true), getBoolean(replantingSection, "canGetFromEnchantingTable", true),
getTagsFromList(getStringList( getTagsFromList(getStringList(
replantingSection, replantingSection,
"supportedItemTags", "supportedItemTags",
@@ -133,16 +127,84 @@ public class EnchantioConfig {
"#minecraft:hoes" "#minecraft:hoes"
) )
)) ))
)); );
if (getBoolean(replantingSection, "enabled", true)) {
ENCHANTS.put(ReplantingEnchant.KEY, replantingEnchant);
}
ConfigurationSection executionerSection = enchantsSection.getConfigurationSection("executioner");
if (executionerSection == null) {
executionerSection = enchantsSection.createSection("executioner");
}
ExecutionerEnchant executionerEnchant = new ExecutionerEnchant(
getInt(executionerSection, "anvilCost", 1),
getInt(executionerSection, "weight", 10),
EnchantmentRegistryEntry.EnchantmentCost.of(
getInt(executionerSection, "minimumCost.base", 40),
getInt(executionerSection, "minimumCost.additionalPerLevel", 3)
),
EnchantmentRegistryEntry.EnchantmentCost.of(
getInt(executionerSection, "maximumCost.base", 65),
getInt(executionerSection,"maximumCost.additionalPerLevel", 1)
),
getBoolean(executionerSection, "canGetFromEnchantingTable", true),
getTagsFromList(getStringList(
executionerSection,
"supportedItemTags",
List.of(
"#minecraft:enchantable/weapon"
)
)),
getInt(executionerSection,"maxLevel", 5),
getDouble(executionerSection, "damageMultiplierPerLevel", 0.05),
getDouble(executionerSection, "maxDamageHpThreshold", 0.25)
);
if (getBoolean(executionerSection, "enabled", true)) {
ENCHANTS.put(ExecutionerEnchant.KEY, executionerEnchant);
}
configuration.save(configFile);
} }
private List<String> getStringList(ConfigurationSection section, String key, List<String> defaultValue) { private List<String> getStringList(ConfigurationSection section, String key, List<String> defaultValue) {
List<String> list = section.contains(key) ? section.getStringList(key) : null; List<String> list = section.contains(key) ? section.getStringList(key) : null;
if (list == null) return defaultValue; if (list == null) {
section.set(key, defaultValue);
return defaultValue;
}
return list; return list;
} }
private int getInt(ConfigurationSection section, String key, int defaultValue) {
int value = section.contains(key) ? section.getInt(key) : -1;
if (value == -1) {
section.set(key, defaultValue);
return defaultValue;
}
return value;
}
private double getDouble(ConfigurationSection section, String key, double defaultValue) {
double value = section.contains(key) ? section.getDouble(key) : -1;
if (value == -1) {
section.set(key, defaultValue);
return defaultValue;
}
return value;
}
private boolean getBoolean(ConfigurationSection section, String key, boolean defaultValue) {
boolean value = section.contains(key) && section.getBoolean(key);
if (!value) {
section.set(key, defaultValue);
return defaultValue;
}
return true;
}
private Set<TagEntry<ItemType>> getTagsFromList(List<String> tags) { private Set<TagEntry<ItemType>> getTagsFromList(List<String> tags) {
Set<TagEntry<ItemType>> supportedItemTags = new HashSet<>(); Set<TagEntry<ItemType>> supportedItemTags = new HashSet<>();
for (String itemTag : tags) { for (String itemTag : tags) {
@@ -0,0 +1,112 @@
package me.youhavetrouble.enchantio.enchants;
import io.papermc.paper.registry.RegistryKey;
import io.papermc.paper.registry.data.EnchantmentRegistryEntry;
import io.papermc.paper.registry.tag.TagKey;
import io.papermc.paper.tag.TagEntry;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.text.Component;
import org.bukkit.inventory.EquipmentSlotGroup;
import org.bukkit.inventory.ItemType;
import java.util.Set;
@SuppressWarnings("UnstableApiUsage")
public class ExecutionerEnchant implements EnchantioEnchant {
public static final Key KEY = Key.key("enchantio:executioner");
private final int anvilCost, weight, maxLevel;
private final EnchantmentRegistryEntry.EnchantmentCost minimumCost;
private final EnchantmentRegistryEntry.EnchantmentCost maximumCost;
private final boolean canGetFromEnchantingTable;
private final Set<TagEntry<ItemType>> supportedItemTags;
private final double damageMultiplierPerLevel, maxDamageHpThreshold;
public ExecutionerEnchant(
int anvilCost,
int weight,
EnchantmentRegistryEntry.EnchantmentCost minimumCost,
EnchantmentRegistryEntry.EnchantmentCost maximumCost,
boolean canGetFromEnchantingTable,
Set<TagEntry<ItemType>> supportedItemTags,
int maxLevel,
double damageMultiplierPerLevel,
double maxDamageHpThreshold
) {
this.anvilCost = anvilCost;
this.weight = weight;
this.minimumCost = minimumCost;
this.maximumCost = maximumCost;
this.canGetFromEnchantingTable = canGetFromEnchantingTable;
this.supportedItemTags = supportedItemTags;
this.maxLevel = maxLevel;
this.damageMultiplierPerLevel = damageMultiplierPerLevel;
this.maxDamageHpThreshold = maxDamageHpThreshold;
}
@Override
public Key getKey() {
return KEY;
}
@Override
public Component getDescription() {
return Component.translatable("enchantio.enchant.executioner","Executioner");
}
@Override
public int getAnvilCost() {
return anvilCost;
}
@Override
public int getMaxLevel() {
return maxLevel;
}
public double getDamageMultiplierPerLevel() {
return damageMultiplierPerLevel;
}
public double getMaxDamageHpThreshold() {
return maxDamageHpThreshold;
}
@Override
public int getWeight() {
return weight;
}
@Override
public EnchantmentRegistryEntry.EnchantmentCost getMinimumCost() {
return minimumCost;
}
@Override
public EnchantmentRegistryEntry.EnchantmentCost getMaximumCost() {
return maximumCost;
}
@Override
public Iterable<EquipmentSlotGroup> getActiveSlots() {
return Set.of(EquipmentSlotGroup.HAND);
}
@Override
public boolean canGetFromEnchantingTable() {
return canGetFromEnchantingTable;
}
@Override
public TagKey<ItemType> getTagForSupportedItems() {
return TagKey.create(RegistryKey.ITEM, Key.key("enchantio:executioner_enchantable"));
}
@Override
public Set<TagEntry<ItemType>> getSupportedItems() {
return supportedItemTags;
}
}
@@ -0,0 +1,60 @@
package me.youhavetrouble.enchantio.listeners;
import io.papermc.paper.registry.RegistryAccess;
import io.papermc.paper.registry.RegistryKey;
import me.youhavetrouble.enchantio.EnchantioConfig;
import me.youhavetrouble.enchantio.enchants.EnchantioEnchant;
import me.youhavetrouble.enchantio.enchants.ExecutionerEnchant;
import org.bukkit.Registry;
import org.bukkit.attribute.Attribute;
import org.bukkit.attribute.AttributeInstance;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.inventory.EntityEquipment;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
@SuppressWarnings("UnstableApiUsage")
public class ExecutionerListener implements Listener {
private final Registry<Enchantment> registry = RegistryAccess.registryAccess().getRegistry(RegistryKey.ENCHANTMENT);
private final Enchantment executioner = registry.get(ExecutionerEnchant.KEY);
@EventHandler(ignoreCancelled = true, priority = EventPriority.NORMAL)
public void onExecutionerDamage(EntityDamageByEntityEvent event) {
if (executioner == null) return;
EnchantioEnchant enchant = EnchantioConfig.ENCHANTS.get(ExecutionerEnchant.KEY);
if (!(enchant instanceof ExecutionerEnchant executionerEnchant)) return;
Entity damager = event.getDamageSource().getCausingEntity();
if (damager == null) return;
if (!damager.equals(event.getDamageSource().getDirectEntity())) return;
if (!(damager instanceof InventoryHolder inventoryHolder)) return;
if (!(inventoryHolder.getInventory() instanceof EntityEquipment entityEquipment)) return;
ItemStack attackingItem = entityEquipment.getItemInMainHand();
if (!attackingItem.containsEnchantment(executioner)) return;
Entity target = event.getEntity();
if (!(target instanceof LivingEntity livingEntity)) return;
AttributeInstance maxHealthAttribute = livingEntity.getAttribute(Attribute.GENERIC_MAX_HEALTH);
if (maxHealthAttribute == null) return;
double targetMaxHealth = maxHealthAttribute.getValue();
double targetHealthPercentage = livingEntity.getHealth() / targetMaxHealth;
if (targetHealthPercentage < executionerEnchant.getMaxDamageHpThreshold()) {
double damageMultiplier = 1 + (executionerEnchant.getDamageMultiplierPerLevel() * attackingItem.getEnchantmentLevel(executioner));
event.setDamage(event.getDamage() * damageMultiplier);
}
}
}
-42
View File
@@ -1,42 +0,0 @@
enchants:
soulbound:
enabled: true
canGetFromEnchantingTable: true
anvilCost: 1
weight: 10
minimumCost:
base: 10
additionalPerLevel: 1
maximumCost:
base: 65
additionalPerLevel: 1
supportedItemTags:
- "#minecraft:enchantable/armor"
- "#minecraft:enchantable/weapon"
- "#minecraft:enchantable/mining"
telepathy:
enabled: true
canGetFromEnchantingTable: true
anvilCost: 1
weight: 5
minimumCost:
base: 15
additionalPerLevel: 1
maximumCost:
base: 65
additionalPerLevel: 1
supportedItemTags:
- "#minecraft:enchantable/mining"
replanting:
enabled: true
canGetFromEnchantingTable: true
anvilCost: 1
weight: 10
minimumCost:
base: 1
additionalPerLevel: 1
maximumCost:
base: 65
additionalPerLevel: 1
supportedItemTags:
- "#minecraft:hoes"