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(