diff --git a/src/main/java/com/cjcrafter/armormechanics/ArmorMechanics.kt b/src/main/java/com/cjcrafter/armormechanics/ArmorMechanics.kt index c67f8e6..f121d80 100644 --- a/src/main/java/com/cjcrafter/armormechanics/ArmorMechanics.kt +++ b/src/main/java/com/cjcrafter/armormechanics/ArmorMechanics.kt @@ -1,19 +1,23 @@ package com.cjcrafter.armormechanics import com.cjcrafter.armormechanics.commands.Command +import com.cjcrafter.armormechanics.durability.registerDurabilityPlaceholders import com.cjcrafter.armormechanics.listeners.* import listeners.ArmorEquipListener import me.cjcrafter.auto.UpdateChecker import me.cjcrafter.auto.UpdateInfo +import me.deecaad.core.MechanicsCore import me.deecaad.core.events.QueueSerializerEvent import me.deecaad.core.file.BukkitConfig import me.deecaad.core.file.SerializeData import me.deecaad.core.file.SerializerException import me.deecaad.core.file.TaskChain +import me.deecaad.core.lib.adventure.text.serializer.legacy.LegacyComponentSerializer import me.deecaad.core.utils.Debugger import me.deecaad.core.utils.FileUtil import me.deecaad.core.utils.LogLevel import me.deecaad.core.utils.ReflectionUtil +import me.deecaad.core.utils.StringUtil import org.bstats.bukkit.Metrics import org.bstats.charts.SimplePie import org.bukkit.Bukkit @@ -51,6 +55,7 @@ class ArmorMechanics : JavaPlugin() { server.pluginManager.disablePlugin(this) return } + registerDurabilityPlaceholders() } override fun onEnable() { @@ -62,6 +67,7 @@ class ArmorMechanics : JavaPlugin() { pm.registerEvents(ArmorUpdateListener(), this) pm.registerEvents(BlockPlaceListener(), this) pm.registerEvents(DamageMechanicListener(), this) + pm.registerEvents(DurabilityListener(), this) pm.registerEvents(ImmunePotionCanceller(), this) pm.registerEvents(PreventRemoveListener(), this) @@ -100,6 +106,9 @@ class ArmorMechanics : JavaPlugin() { }) .thenRunSync(Runnable { reloadConfig() + // Reload durability prefix and format + DURABILITY_PREFIX = LegacyComponentSerializer.legacySection().serialize(MechanicsCore.getPlugin().message.deserialize(StringUtil.colorAdventure(config.getString("Durability_Prefix"))!!)) + DURABILITY_FORMAT = StringUtil.colorAdventure(config.getString("Durability_Format"))!! // Clear old data effects.clear() @@ -202,5 +211,7 @@ class ArmorMechanics : JavaPlugin() { companion object { lateinit var INSTANCE: ArmorMechanics + lateinit var DURABILITY_PREFIX: String + lateinit var DURABILITY_FORMAT: String } } diff --git a/src/main/java/com/cjcrafter/armormechanics/ArmorSerializer.kt b/src/main/java/com/cjcrafter/armormechanics/ArmorSerializer.kt index 5fba202..c769fe1 100644 --- a/src/main/java/com/cjcrafter/armormechanics/ArmorSerializer.kt +++ b/src/main/java/com/cjcrafter/armormechanics/ArmorSerializer.kt @@ -1,10 +1,13 @@ package com.cjcrafter.armormechanics -import me.deecaad.core.compatibility.CompatibilityAPI +import com.cjcrafter.armormechanics.durability.DurabilityManager.setDurability +import com.cjcrafter.armormechanics.durability.DurabilityManager.setMaxDurability import me.deecaad.core.file.SerializeData import me.deecaad.core.file.SerializerException import me.deecaad.core.file.serializers.ItemSerializer -import me.deecaad.core.utils.AdventureUtil +import me.deecaad.core.lib.adventure.text.serializer.legacy.LegacyComponentSerializer +import me.deecaad.core.placeholder.PlaceholderData +import me.deecaad.core.placeholder.PlaceholderMessage import me.deecaad.weaponmechanics.utils.CustomTag import org.bukkit.inventory.ItemStack import javax.annotation.Nonnull @@ -20,6 +23,29 @@ class ArmorSerializer : ItemSerializer() { "Material was not a valid armor type", SerializerException.forValue(item.type) ) + + + if (data.has("Item_Durability")) { + val durability = data.of("Item_Durability").getInt(0) + if (durability > 0) { + setDurability(item, durability) + setMaxDurability(item, durability) + val meta = item.itemMeta + val lore = ArrayList() + if (meta!!.hasLore()) { + lore.addAll(meta.lore!!) + } + val placeholderMessage = PlaceholderMessage(ArmorMechanics.DURABILITY_FORMAT) + val component = placeholderMessage.replaceAndDeserialize(PlaceholderData.of(null, item, null, null)) + val legacy = LegacyComponentSerializer.legacySection().serialize(component) + lore.add(ArmorMechanics.DURABILITY_PREFIX + legacy) + meta.lore = lore + item.itemMeta = meta + } + } + + + val title = data.key val effect = data.of("Bonus_Effects").serialize(BonusEffect::class.java) diff --git a/src/main/java/com/cjcrafter/armormechanics/durability/DurabilityManager.kt b/src/main/java/com/cjcrafter/armormechanics/durability/DurabilityManager.kt new file mode 100644 index 0000000..d7932e7 --- /dev/null +++ b/src/main/java/com/cjcrafter/armormechanics/durability/DurabilityManager.kt @@ -0,0 +1,72 @@ +package com.cjcrafter.armormechanics.durability + +import com.cjcrafter.armormechanics.ArmorMechanics +import me.deecaad.core.compatibility.CompatibilityAPI +import me.deecaad.core.lib.adventure.text.serializer.legacy.LegacyComponentSerializer +import me.deecaad.core.placeholder.PlaceholderData +import me.deecaad.core.placeholder.PlaceholderMessage +import org.bukkit.inventory.ItemStack +import org.bukkit.inventory.meta.Damageable +import org.bukkit.inventory.meta.ItemMeta + +object DurabilityManager { + fun changeDurability(item: ItemStack, change: Int) { + if (getDurability(item) == -1) { + return + } + var durability = getDurability(item) + val maxDurability = getMaxDurability(item) + durability = Math.min(durability + change, maxDurability) + setDurability(item, durability) + val damageable = item.itemMeta + if (damageable !is Damageable) { + return + } + + if (durability > 0) { + damageable.damage = item.type.maxDurability - (durability * item.type.maxDurability) / maxDurability + if ((damageable as ItemMeta).hasLore()) { + val lore: ArrayList = ArrayList((damageable as ItemMeta).lore!!) + val loreClone = lore.toMutableList() + val index = loreClone.indexOfFirst { it.startsWith(ArmorMechanics.DURABILITY_PREFIX) } + val placeholderMessage = PlaceholderMessage(ArmorMechanics.DURABILITY_FORMAT) + val component = placeholderMessage.replaceAndDeserialize(PlaceholderData.of(null, item, null, null)) + val legacy = LegacyComponentSerializer.legacySection().serialize(component) + loreClone[index] = ArmorMechanics.DURABILITY_PREFIX + legacy + (damageable as ItemMeta).lore = loreClone + } else { + (damageable as ItemMeta).lore = listOf( + ArmorMechanics.DURABILITY_PREFIX + LegacyComponentSerializer.legacySection().serialize( + PlaceholderMessage( + ArmorMechanics.DURABILITY_FORMAT + ).replaceAndDeserialize(PlaceholderData.of(null, item, null, null)) + ) + ) + } + } else { + damageable.damage = item.type.maxDurability.toInt() + item.amount = 0 + } + item.itemMeta = damageable + } + + fun getMaxDurability(item: ItemStack): Int { + return if (CompatibilityAPI.getNBTCompatibility().hasInt(item, "ArmorMechanics", "armor-durability-max")) { + CompatibilityAPI.getNBTCompatibility().getInt(item, "ArmorMechanics", "armor-durability-max") + } else -1 + } + + fun getDurability(item: ItemStack): Int { + return if (CompatibilityAPI.getNBTCompatibility().hasInt(item, "ArmorMechanics", "armor-durability")) { + CompatibilityAPI.getNBTCompatibility().getInt(item, "ArmorMechanics", "armor-durability") + } else -1 + } + + fun setMaxDurability(item: ItemStack, value: Int) { + CompatibilityAPI.getNBTCompatibility().setInt(item, "ArmorMechanics", "armor-durability-max", value) + } + + fun setDurability(item: ItemStack, value: Int) { + CompatibilityAPI.getNBTCompatibility().setInt(item, "ArmorMechanics", "armor-durability", value) + } +} \ No newline at end of file diff --git a/src/main/java/com/cjcrafter/armormechanics/durability/DurabilityPlaceholderHandler.kt b/src/main/java/com/cjcrafter/armormechanics/durability/DurabilityPlaceholderHandler.kt new file mode 100644 index 0000000..36e8b18 --- /dev/null +++ b/src/main/java/com/cjcrafter/armormechanics/durability/DurabilityPlaceholderHandler.kt @@ -0,0 +1,21 @@ +package com.cjcrafter.armormechanics.durability + +import me.deecaad.core.placeholder.NumericPlaceholderHandler +import me.deecaad.core.placeholder.PlaceholderData +import me.deecaad.core.placeholder.PlaceholderHandler + +fun registerDurabilityPlaceholders() { + PlaceholderHandler.REGISTRY.add(DurabilityPlaceholderHandler()) + PlaceholderHandler.REGISTRY.add(MaxDurabilityPlaceholderHandler()) +} +class DurabilityPlaceholderHandler : NumericPlaceholderHandler("durability_current") { + override fun requestValue(p0: PlaceholderData): Number? { + return DurabilityManager.getDurability(p0.item()!!) + } +} + +class MaxDurabilityPlaceholderHandler : NumericPlaceholderHandler("durability_max") { + override fun requestValue(p0: PlaceholderData): Number? { + return DurabilityManager.getMaxDurability(p0.item()!!) + } +} \ No newline at end of file diff --git a/src/main/java/com/cjcrafter/armormechanics/listeners/DurabilityListener.kt b/src/main/java/com/cjcrafter/armormechanics/listeners/DurabilityListener.kt new file mode 100644 index 0000000..3cb485d --- /dev/null +++ b/src/main/java/com/cjcrafter/armormechanics/listeners/DurabilityListener.kt @@ -0,0 +1,66 @@ +package com.cjcrafter.armormechanics.listeners + +import com.cjcrafter.armormechanics.durability.DurabilityManager.changeDurability +import com.cjcrafter.armormechanics.durability.DurabilityManager.getDurability +import me.deecaad.core.compatibility.CompatibilityAPI +import org.bukkit.Material +import org.bukkit.event.EventHandler +import org.bukkit.event.EventPriority +import org.bukkit.event.Listener +import org.bukkit.event.inventory.PrepareAnvilEvent +import org.bukkit.event.inventory.PrepareItemCraftEvent +import org.bukkit.event.player.PlayerItemDamageEvent +import org.bukkit.event.player.PlayerItemMendEvent +import org.bukkit.inventory.ItemStack + +class DurabilityListener : Listener { + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + fun onItemDamaged(event: PlayerItemDamageEvent) { + changeDurability(event.item, -event.damage) + } + + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + fun onItemMend(event: PlayerItemMendEvent) { + changeDurability(event.item, event.repairAmount) + } + + @EventHandler + fun onRepairAnvil(event: PrepareAnvilEvent) { + val (names, totalDurability, result) = namesTotalDurabilityAndResult(event.inventory.contents) ?: return // Returns if ArmorMechanics is not involved + if (names.size != 1) { + event.result = null // We can set result to null because we are sure about ArmorMechanics is involved + return + } + changeDurability(result, totalDurability - getDurability(result)) + event.result = result + } + + @EventHandler + fun onRepairCraft(event: PrepareItemCraftEvent) { + if (!event.isRepair) return + val (names, totalDurability, result) = namesTotalDurabilityAndResult(event.inventory.matrix) ?: return // Returns if ArmorMechanics is not involved + if (names.size != 1) { + event.inventory.result = null // We can set result to null because we are sure about ArmorMechanics is involved + return + } + changeDurability(result, totalDurability - getDurability(result)) + event.inventory.result = result + } + + private fun namesTotalDurabilityAndResult(items: Array): Triple, Int, ItemStack>? { + var totalDurability = 0 + val names = mutableSetOf() + var resultItem: ItemStack? = null + for (item in items) { + if (item == null || item.type == Material.AIR) continue + val durability = getDurability(item) + if (durability == -1) return null // An unknown item is involved + if (resultItem == null) resultItem = item.clone() + totalDurability += durability + val name = CompatibilityAPI.getNBTCompatibility().getString(item, "ArmorMechanics", "armor-title") ?: return null // Returns if ArmorMechanics is not involved + names += name + } + if (resultItem == null) return null // None of the items have custom durability + return Triple(names, totalDurability, resultItem) + } +} \ No newline at end of file diff --git a/src/main/resources/ArmorMechanics/config.yml b/src/main/resources/ArmorMechanics/config.yml index 855057a..c5ec5e1 100644 --- a/src/main/resources/ArmorMechanics/config.yml +++ b/src/main/resources/ArmorMechanics/config.yml @@ -32,4 +32,9 @@ Delete_Old_Armor: false # Whenever you update your armor, I *HIGHLY SUGGEST* you run a /restart on your # server instead of just a /am reload. This will force all players to relog and # their armor will be updated. -Update_Armor: true \ No newline at end of file +Update_Armor: true + +# These settings are for setting maximum durability for armor. +# You can use Item_Durability attribute on armors to use this feature. +Durability_Prefix: 'Durability: ' +Durability_Format: "/" \ No newline at end of file