From 015ca99475c8ca392d80c073153512cc53fde15e Mon Sep 17 00:00:00 2001 From: alexcrea Date: Sat, 20 Jun 2026 04:36:02 +0200 Subject: [PATCH 1/4] anvil sound & degrade basic feature # Conflicts: # src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt --- .../cuanvil/listener/AnvilResultListener.kt | 58 ++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt index e9290ac9..75fe607d 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt @@ -4,7 +4,9 @@ import io.delilaheve.CustomAnvil import io.delilaheve.util.ConfigOptions import io.delilaheve.util.ItemUtil.canMergeWith import org.bukkit.GameMode +import org.bukkit.Location import org.bukkit.Material +import org.bukkit.Sound import org.bukkit.entity.Player import org.bukkit.event.Event import org.bukkit.event.EventHandler @@ -354,7 +356,9 @@ class AnvilResultListener : Listener { player.inventory.setItem(slotDestination.slot, result.item) } - // TODO probably anvil damage & sound here ?? + if (event.click != ClickType.MIDDLE) + handleAnvilMechanic(view, player.gameMode != GameMode.CREATIVE) + return true } @@ -382,6 +386,58 @@ class AnvilResultListener : Listener { } } + // Process both sound & degradation + private fun handleAnvilMechanic(view: AnvilView, canDegrade: Boolean) { + // ok so we do not provide a getLocation on view ? + val inv = view.topInventory as? AnvilInventory + val location = inv?.location + + val wasDestroyed = canDegrade && tryDegradeAnvil(view, location) + tryPlaySound(location, wasDestroyed) + } + + private fun tryDegradeAnvil(view: AnvilView, location: Location?): Boolean { + val world = location?.world ?: return false + if (Math.random() > 0.12) return false //TODO config value instead of hardcoded + + val block = world.getBlockAt(location) + + val next = when (block.type) { + Material.ANVIL -> Material.CHIPPED_ANVIL + Material.CHIPPED_ANVIL -> Material.DAMAGED_ANVIL + Material.DAMAGED_ANVIL -> Material.AIR + else -> return false + } + + block.type = next + + if (next == Material.AIR) { + view.close() + return true + } + + return false + } + + private fun tryPlaySound( + location: Location?, + wasDestroyed: Boolean, + ) { + val world = location?.world ?: return + + if (false) return //TODO enabled config sound + + // TODO config values + world.playSound( + location, + if (wasDestroyed) + Sound.BLOCK_ANVIL_DESTROY + else + Sound.BLOCK_ANVIL_USE, + 1f, 1f + ) + } + private fun onUnitRepairExtract( rightItem: ItemStack, event: InventoryClickEvent, From 3dd706b4a80d02beb0d589cc13fe61a373b32a81 Mon Sep 17 00:00:00 2001 From: alexcrea Date: Sat, 20 Jun 2026 05:43:23 +0200 Subject: [PATCH 2/4] add sound & degradation in config --- defaultconfigs/1.21.11/config.yml | 26 ++++++++++++++++++++++++++ defaultconfigs/1.21.9/config.yml | 27 +++++++++++++++++++++++++++ defaultconfigs/1.21/config.yml | 27 +++++++++++++++++++++++++++ src/main/resources/config.yml | 26 ++++++++++++++++++++++++++ 4 files changed, 106 insertions(+) diff --git a/defaultconfigs/1.21.11/config.yml b/defaultconfigs/1.21.11/config.yml index c3ea6204..22de446f 100644 --- a/defaultconfigs/1.21.11/config.yml +++ b/defaultconfigs/1.21.11/config.yml @@ -476,6 +476,32 @@ monetary_cost: work_penalty: 1.0 # for work penalty (aka use penalty) recipe: 1.0 # for custom anvil recipe cost +# Chance that an anvil get degraded +# set it to zero if you wish to disable it +anvil_degradation: 0.12 + +# see https://www.digminecraft.com/lists/sound_list_pc.php +# for a list of most of the sounds +anvil_sound: + enabled: true + # The sound are weighted so if there is multiples of them one is selected randomly + # so if you want to add a "rare sound" as an easter egg you can ! + # If invalid, it will be removed from the list. and if empty it will use the default + use: + default: + sound: block.anvil.use + category: blocks + volume: 1.0 + pitch: 1.0 + weight: 1.0 + destroy: + default: + sound: block.anvil.destroy + category: blocks + volume: 1.0 + pitch: 1.0 + weight: 1.0 + # Whether to show debug logging debug_log: false diff --git a/defaultconfigs/1.21.9/config.yml b/defaultconfigs/1.21.9/config.yml index 614c8df0..4d1ae9d6 100644 --- a/defaultconfigs/1.21.9/config.yml +++ b/defaultconfigs/1.21.9/config.yml @@ -468,6 +468,33 @@ monetary_cost: work_penalty: 1.0 # for work penalty (aka use penalty) recipe: 1.0 # for custom anvil recipe cost +# Chance that an anvil get degraded +# set it to zero if you wish to disable it +anvil_degradation: 0.12 + +# see https://www.digminecraft.com/lists/sound_list_pc.php +# for a list of most of the sounds +anvil_sound: + enabled: true + # The sound are weighted so if there is multiples of them one is selected randomly + # so if you want to add a "rare sound" as an easter egg you can ! + # If invalid, it will be removed from the list. and if empty it will use the default + use: + default: + sound: block.anvil.use + category: blocks + volume: 1.0 + pitch: 1.0 + weight: 1.0 + destroy: + default: + sound: block.anvil.destroy + category: blocks + volume: 1.0 + pitch: 1.0 + weight: 1.0 + + # Whether to show debug logging debug_log: false diff --git a/defaultconfigs/1.21/config.yml b/defaultconfigs/1.21/config.yml index a0bcbd86..50b94f1b 100644 --- a/defaultconfigs/1.21/config.yml +++ b/defaultconfigs/1.21/config.yml @@ -456,6 +456,33 @@ monetary_cost: work_penalty: 1.0 # for work penalty (aka use penalty) recipe: 1.0 # for custom anvil recipe cost +# Chance that an anvil get degraded +# set it to zero if you wish to disable it +anvil_degradation: 0.12 + +# see https://www.digminecraft.com/lists/sound_list_pc.php +# for a list of most of the sounds +anvil_sound: + enabled: true + # The sound are weighted so if there is multiples of them one is selected randomly + # so if you want to add a "rare sound" as an easter egg you can ! + # If invalid, it will be removed from the list. and if empty it will use the default + use: + default: + sound: block.anvil.use + category: blocks + volume: 1.0 + pitch: 1.0 + weight: 1.0 + destroy: + default: + sound: block.anvil.destroy + category: blocks + volume: 1.0 + pitch: 1.0 + weight: 1.0 + + # Whether to show debug logging debug_log: false diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 6d946d4d..218c3a1f 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -463,6 +463,32 @@ monetary_cost: work_penalty: 1.0 # for work penalty (aka use penalty) recipe: 1.0 # for custom anvil recipe cost +# Chance that an anvil get degraded +# set it to zero if you wish to disable it +anvil_degradation: 0.12 + +# see https://www.digminecraft.com/lists/sound_list_pc.php +# for a list of most of the sounds +anvil_sound: + enabled: true + # The sound are weighted so if there is multiples of them one is selected randomly + # so if you want to add a "rare sound" as an easter egg you can ! + # If invalid, it will be removed from the list. and if empty it will use the default + use: + default: + sound: block.anvil.use + category: blocks + volume: 1.0 + pitch: 1.0 + weight: 1.0 + destroy: + default: + sound: block.anvil.destroy + category: blocks + volume: 1.0 + pitch: 1.0 + weight: 1.0 + # Whether to show debug logging debug_log: false From c38e8025cde633a77e3b4fda494f9ce1b00285cc Mon Sep 17 00:00:00 2001 From: alexcrea Date: Sat, 20 Jun 2026 13:54:43 +0200 Subject: [PATCH 3/4] sound & anvil damage done --- .../cuanvil/config/AnvilFinishOptions.kt | 146 ++++++++++++++++++ .../cuanvil/listener/AnvilResultListener.kt | 43 ++++-- 2 files changed, 173 insertions(+), 16 deletions(-) create mode 100644 src/main/kotlin/xyz/alexcrea/cuanvil/config/AnvilFinishOptions.kt diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/config/AnvilFinishOptions.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/config/AnvilFinishOptions.kt new file mode 100644 index 00000000..4cda8d83 --- /dev/null +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/config/AnvilFinishOptions.kt @@ -0,0 +1,146 @@ +package xyz.alexcrea.cuanvil.config + +import io.delilaheve.CustomAnvil +import org.bukkit.NamespacedKey +import org.bukkit.Registry +import org.bukkit.Sound +import org.bukkit.SoundCategory +import org.bukkit.configuration.ConfigurationSection +import java.util.Random +import java.util.logging.Level + +object AnvilFinishOptions { + + data class ConfiguredSound( + val sound: Sound, + val category: SoundCategory, + val volume: Float, + val pitch: Float, + val weight: Double, + ) + + // Paths + private const val SOUND_ENABLED = "anvil_sound.enabled" + + private const val USE_SOUNDS_ROOT = "anvil_sound.use" + private const val DESTROY_SOUNDS_ROOT = "anvil_sound.destroy" + + private const val ANVIL_DEGRADATION_PATH = "anvil_degradation" + + // Defaults + private const val DEFAULT_SOUND_ENABLED = true + private val DEFAULT_USE_SOUND = ConfiguredSound( + Sound.BLOCK_ANVIL_USE, + SoundCategory.BLOCKS, + 1.0f, + 1.0f, + 1.0, + ) + + private val DEFAULT_DESTROY_SOUND = ConfiguredSound( + Sound.BLOCK_ANVIL_DESTROY, + SoundCategory.BLOCKS, + 1.0f, + 1.0f, + 1.0, + ) + private const val DEFAULT_ANVIL_DEGRADATION_PATH = 0.12 + + // Others + private val RANDOM = Random() + + // Fetch + val sound_enabled: Boolean + get() { + return ConfigHolder.DEFAULT_CONFIG + .config + .getBoolean(SOUND_ENABLED, DEFAULT_SOUND_ENABLED) + } + + fun getAnvilSound(wasDestroyed: Boolean): ConfiguredSound { + return if(wasDestroyed) + getRandomDestroySound() + else getRandomUseSound() + } + + val degradation_chance: Double + get() { + return ConfigHolder.DEFAULT_CONFIG + .config + .getDouble(ANVIL_DEGRADATION_PATH, DEFAULT_ANVIL_DEGRADATION_PATH) + } + + // Utils + private fun getRandomUseSound(): ConfiguredSound { + return fromListing(USE_SOUNDS_ROOT, DEFAULT_USE_SOUND) + } + + private fun getRandomDestroySound(): ConfiguredSound { + return fromListing(DESTROY_SOUNDS_ROOT, DEFAULT_DESTROY_SOUND) + } + + private fun fromListing(path: String, default: ConfiguredSound): ConfiguredSound { + val section = ConfigHolder.DEFAULT_CONFIG + .config.getConfigurationSection(path) ?: return default + + val list = readSounds(section) + return weightedRandom(list, default) + } + + private fun readSounds(section: ConfigurationSection): List { + val sounds = ArrayList() + + for (key in section.getKeys(false)) { + if(!section.isConfigurationSection(key)) continue + + val result = readSound(section.getConfigurationSection(key)!!) + if(result == null) { + CustomAnvil.instance.logger.warning("Couldn't read sounds for key $key") + continue + } + + sounds.add(result) + } + return sounds + } + + private fun readSound(section: ConfigurationSection): ConfiguredSound? { + val name = section.getString("sound", null)?: return null + val key = NamespacedKey.fromString(name)?: return null + val sound = Registry.SOUNDS.get(key)?: return null + + val categoryName = section.getString("category", null)?: return null + val category = try { + SoundCategory.valueOf(categoryName.uppercase()) + } catch (_: IllegalArgumentException) { + return null + } + + val volume = section.getDouble("volume", 1.0).toFloat() + val pitch = section.getDouble("pitch", 1.0).toFloat() + val weight = section.getDouble("weight", 1.0) + if(weight <= 0) return null + + return ConfiguredSound(sound, category, volume, pitch, weight) + } + + private fun weightedRandom(sounds: List, default: ConfiguredSound): ConfiguredSound { + if(sounds.isEmpty()) return default + + val weightSum = sounds.sumOf { it.weight } + if(weightSum == .0) return sounds.first() + + val selectedWeight = RANDOM.nextDouble(weightSum) + + var currentSum = .0 + sounds.forEach { + currentSum += it.weight + if(currentSum > selectedWeight) return it + } + + // What ? how + CustomAnvil.instance.logger.log(Level.SEVERE, "Something very... very wrong happen on sound handling. please report this to the developer") + return default + } + +} \ No newline at end of file diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt index 75fe607d..9868b106 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt @@ -6,7 +6,7 @@ import io.delilaheve.util.ItemUtil.canMergeWith import org.bukkit.GameMode import org.bukkit.Location import org.bukkit.Material -import org.bukkit.Sound +import org.bukkit.entity.HumanEntity import org.bukkit.entity.Player import org.bukkit.event.Event import org.bukkit.event.EventHandler @@ -23,6 +23,7 @@ import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic.AnvilResult import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic.CustomCraftResult import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic.LoreEditResult import xyz.alexcrea.cuanvil.anvil.AnvilMergeLogic.UnitRepairResult +import xyz.alexcrea.cuanvil.config.AnvilFinishOptions import xyz.alexcrea.cuanvil.dependency.DependencyManager import xyz.alexcrea.cuanvil.dependency.economy.EconomyManager import xyz.alexcrea.cuanvil.dependency.util.PlatformUtil.setComponentDisplayName @@ -357,7 +358,7 @@ class AnvilResultListener : Listener { } if (event.click != ClickType.MIDDLE) - handleAnvilMechanic(view, player.gameMode != GameMode.CREATIVE) + handleAnvilMechanic(player, view, player.gameMode != GameMode.CREATIVE) return true } @@ -387,18 +388,18 @@ class AnvilResultListener : Listener { } // Process both sound & degradation - private fun handleAnvilMechanic(view: AnvilView, canDegrade: Boolean) { + private fun handleAnvilMechanic(player: HumanEntity, view: AnvilView, canDegrade: Boolean) { // ok so we do not provide a getLocation on view ? val inv = view.topInventory as? AnvilInventory val location = inv?.location val wasDestroyed = canDegrade && tryDegradeAnvil(view, location) - tryPlaySound(location, wasDestroyed) + tryPlaySound(location, player, wasDestroyed) } private fun tryDegradeAnvil(view: AnvilView, location: Location?): Boolean { val world = location?.world ?: return false - if (Math.random() > 0.12) return false //TODO config value instead of hardcoded + if (Math.random() > AnvilFinishOptions.degradation_chance) return false val block = world.getBlockAt(location) @@ -421,21 +422,31 @@ class AnvilResultListener : Listener { private fun tryPlaySound( location: Location?, + player: HumanEntity, wasDestroyed: Boolean, ) { - val world = location?.world ?: return + val world = location?.world - if (false) return //TODO enabled config sound + if (!AnvilFinishOptions.sound_enabled) return - // TODO config values - world.playSound( - location, - if (wasDestroyed) - Sound.BLOCK_ANVIL_DESTROY - else - Sound.BLOCK_ANVIL_USE, - 1f, 1f - ) + val sound = AnvilFinishOptions.getAnvilSound(wasDestroyed) + if(world == null) { + player.world.playSound( + player, + sound.sound, + sound.category, + sound.volume, + sound.pitch, + ) + } else { + world.playSound( + location, + sound.sound, + sound.category, + sound.volume, + sound.pitch, + ) + } } private fun onUnitRepairExtract( From 5bd78b4444b585aeaf3b3c06ded4240f77701657 Mon Sep 17 00:00:00 2001 From: alexcrea Date: Sat, 11 Jul 2026 22:51:24 +0200 Subject: [PATCH 4/4] backport anvil mechanic --- .../cuanvil/listener/AnvilResultListener.kt | 40 +++++++------------ 1 file changed, 14 insertions(+), 26 deletions(-) diff --git a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt index 9868b106..dd35ed69 100644 --- a/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt +++ b/src/main/kotlin/xyz/alexcrea/cuanvil/listener/AnvilResultListener.kt @@ -358,7 +358,7 @@ class AnvilResultListener : Listener { } if (event.click != ClickType.MIDDLE) - handleAnvilMechanic(player, view, player.gameMode != GameMode.CREATIVE) + handleAnvilMechanic(player, inventory, player.gameMode != GameMode.CREATIVE) return true } @@ -388,16 +388,15 @@ class AnvilResultListener : Listener { } // Process both sound & degradation - private fun handleAnvilMechanic(player: HumanEntity, view: AnvilView, canDegrade: Boolean) { + private fun handleAnvilMechanic(player: HumanEntity, inventory: AnvilInventory, canDegrade: Boolean) { // ok so we do not provide a getLocation on view ? - val inv = view.topInventory as? AnvilInventory - val location = inv?.location + val location = inventory.location - val wasDestroyed = canDegrade && tryDegradeAnvil(view, location) - tryPlaySound(location, player, wasDestroyed) + val wasDestroyed = canDegrade && tryDegradeAnvil(player, location) + tryPlaySound(location, wasDestroyed) } - private fun tryDegradeAnvil(view: AnvilView, location: Location?): Boolean { + private fun tryDegradeAnvil(player: HumanEntity, location: Location?): Boolean { val world = location?.world ?: return false if (Math.random() > AnvilFinishOptions.degradation_chance) return false @@ -413,7 +412,7 @@ class AnvilResultListener : Listener { block.type = next if (next == Material.AIR) { - view.close() + player.closeInventory() return true } @@ -422,7 +421,6 @@ class AnvilResultListener : Listener { private fun tryPlaySound( location: Location?, - player: HumanEntity, wasDestroyed: Boolean, ) { val world = location?.world @@ -430,23 +428,13 @@ class AnvilResultListener : Listener { if (!AnvilFinishOptions.sound_enabled) return val sound = AnvilFinishOptions.getAnvilSound(wasDestroyed) - if(world == null) { - player.world.playSound( - player, - sound.sound, - sound.category, - sound.volume, - sound.pitch, - ) - } else { - world.playSound( - location, - sound.sound, - sound.category, - sound.volume, - sound.pitch, - ) - } + world?.playSound( + location, + sound.sound, + sound.category, + sound.volume, + sound.pitch, + ) } private fun onUnitRepairExtract(