feat: Vein Miner configuration now supports block tags and block highlight colors.

This commit is contained in:
Kariya 2025-12-16 20:03:17 +00:00
parent 32dbebb228
commit a464a8f77f
3 changed files with 152 additions and 103 deletions

View File

@ -15,12 +15,14 @@ object Config {
var componentTickInterval: Long = 1L var componentTickInterval: Long = 1L
// Vein Miner // Vein Miner
data class ToolCategory(val toolPattern: String, val allowedBlockPatterns: List<String>) data class ToolCategory(val tool: String, val blocks: List<String>)
var veinMinerMaxBlocks: Int = 64 var veinMinerMaxBlocks: Int = 64
var veinMinerActivationMode: String = "SNEAK" // "SNEAK", "ALWAYS", "STAND" var veinMinerActivationMode: String = "SNEAK" // "SNEAK", "ALWAYS", "STAND"
val veinMinerCompatibleMaterials: MutableMap<org.bukkit.Material, Set<org.bukkit.Material>> = mutableMapOf() val veinMinerCompatibleMaterials: MutableMap<org.bukkit.Material, Set<org.bukkit.Material>> = mutableMapOf()
val veinMinerBlockColors: MutableMap<org.bukkit.Material, org.bukkit.Color> = mutableMapOf()
val veinMinerToolCategories: MutableList<ToolCategory> = mutableListOf() val veinMinerToolCategories: MutableList<ToolCategory> = mutableListOf()
fun load(plugin: JavaPlugin) { fun load(plugin: JavaPlugin) {
plugin.reloadConfig() plugin.reloadConfig()
val config = plugin.config val config = plugin.config
@ -47,6 +49,7 @@ object Config {
veinMinerActivationMode = config.getString("components.vein_miner.activation_mode", "SNEAK") ?: "SNEAK" veinMinerActivationMode = config.getString("components.vein_miner.activation_mode", "SNEAK") ?: "SNEAK"
loadCompatibleGroups(config) loadCompatibleGroups(config)
loadBlockColors(config)
loadToolCategories(config) loadToolCategories(config)
// Save back to ensure defaults are written if missing // Save back to ensure defaults are written if missing
@ -75,15 +78,32 @@ object Config {
val groups = config.getList("components.vein_miner.compatible_groups") as? List<*> ?: return val groups = config.getList("components.vein_miner.compatible_groups") as? List<*> ?: return
for (groupObj in groups) { for (groupObj in groups) {
val group = groupObj as? List<*> ?: continue val group = when (groupObj) {
val materials = group.mapNotNull { is List<*> -> groupObj
val name = (it as? String) ?: return@mapNotNull null is String -> listOf(groupObj)
try { else -> continue
org.bukkit.Material.valueOf(name.uppercase().removePrefix("MINECRAFT:")) }
} catch (e: IllegalArgumentException) {
null val materials = mutableSetOf<org.bukkit.Material>()
for (it in group) {
val name = (it as? String) ?: continue
if (name.startsWith("#")) {
val key = org.bukkit.NamespacedKey.fromString(name.substring(1))
if (key != null) {
val tag = org.bukkit.Bukkit.getTag(org.bukkit.Tag.REGISTRY_BLOCKS, key, org.bukkit.Material::class.java)
if (tag != null) {
materials.addAll(tag.values)
}
}
} else {
try {
materials.add(org.bukkit.Material.valueOf(name.uppercase().removePrefix("MINECRAFT:")))
} catch (e: IllegalArgumentException) {
// Ignore
}
} }
}.toSet() }
for (mat in materials) { for (mat in materials) {
val existing = veinMinerCompatibleMaterials.getOrDefault(mat, emptySet()) val existing = veinMinerCompatibleMaterials.getOrDefault(mat, emptySet())
@ -97,9 +117,49 @@ object Config {
val list = config.getMapList("components.vein_miner.tool_categories") val list = config.getMapList("components.vein_miner.tool_categories")
for (map in list) { for (map in list) {
val toolPattern = map["tool_pattern"] as? String ?: continue val tool = map["tool"] as? String ?: continue
val allowedBlocks = (map["allowed_blocks"] as? List<*>)?.filterIsInstance<String>() ?: continue val blocks = (map["active_blocks"] as? List<*>)?.filterIsInstance<String>() ?: continue
veinMinerToolCategories.add(ToolCategory(toolPattern, allowedBlocks)) veinMinerToolCategories.add(ToolCategory(tool, blocks))
}
}
private fun loadBlockColors(config: FileConfiguration) {
veinMinerBlockColors.clear()
val section = config.getConfigurationSection("components.vein_miner.block_colors") ?: return
for (keyStr in section.getKeys(false)) {
val hexOrRgb = section.getString(keyStr) ?: continue
val color = try {
if (hexOrRgb.contains(",")) {
val parts = hexOrRgb.split(",").map { it.trim().toInt() }
if (parts.size == 3) org.bukkit.Color.fromRGB(parts[0], parts[1], parts[2]) else null
} else {
val hex = hexOrRgb.removePrefix("#")
org.bukkit.Color.fromRGB(hex.toInt(16))
}
} catch (e: Exception) {
null
}
if (color == null) continue
if (keyStr.startsWith("#")) {
val key = org.bukkit.NamespacedKey.fromString(keyStr.substring(1))
if (key != null) {
val tag = org.bukkit.Bukkit.getTag(org.bukkit.Tag.REGISTRY_BLOCKS, key, org.bukkit.Material::class.java)
if (tag != null) {
for (mat in tag.values) {
veinMinerBlockColors[mat] = color
}
}
}
} else {
try {
val mat = org.bukkit.Material.valueOf(keyStr.uppercase().removePrefix("MINECRAFT:"))
veinMinerBlockColors[mat] = color
} catch (e: IllegalArgumentException) {
continue
}
}
} }
} }
} }

View File

@ -8,6 +8,7 @@ import org.bukkit.NamespacedKey
import org.bukkit.block.Block import org.bukkit.block.Block
import org.bukkit.entity.Player import org.bukkit.entity.Player
import org.bukkit.event.block.BlockBreakEvent import org.bukkit.event.block.BlockBreakEvent
import org.bukkit.event.player.PlayerItemDamageEvent
import org.bukkit.inventory.ItemStack import org.bukkit.inventory.ItemStack
import org.bukkit.persistence.PersistentDataType import org.bukkit.persistence.PersistentDataType
import org.bukkit.plugin.java.JavaPlugin import org.bukkit.plugin.java.JavaPlugin
@ -17,6 +18,9 @@ import org.bukkit.entity.ExperienceOrb
import org.bukkit.entity.BlockDisplay import org.bukkit.entity.BlockDisplay
import org.bukkit.entity.Display import org.bukkit.entity.Display
import org.bukkit.util.Transformation import org.bukkit.util.Transformation
import org.bukkit.map.MapPalette
import org.bukkit.GameMode
import org.bukkit.Tag
import org.joml.Vector3f import org.joml.Vector3f
import org.bukkit.Color import org.bukkit.Color
import org.bukkit.Bukkit import org.bukkit.Bukkit
@ -102,7 +106,6 @@ class VeinMinerComponent(private val plugin: JavaPlugin) : ToolComponent {
// クライアントのパフォーマンスを考慮し、最大ハイライト数を制限しても良いが、 // クライアントのパフォーマンスを考慮し、最大ハイライト数を制限しても良いが、
// ユーザー要望通り全てハイライトする // ユーザー要望通り全てハイライトする
if (!supportsBlockDisplay) return if (!supportsBlockDisplay) return
for (block in blocksToBreak) { for (block in blocksToBreak) {
val loc = block.location.add(0.5, 0.5, 0.5) val loc = block.location.add(0.5, 0.5, 0.5)
try { try {
@ -111,13 +114,13 @@ class VeinMinerComponent(private val plugin: JavaPlugin) : ToolComponent {
e.block = block.blockData e.block = block.blockData
// 少し大きくして元のブロックを覆う // 少し大きくして元のブロックを覆う
e.transformation = Transformation( e.transformation = Transformation(
Vector3f(0f, 0f, 0f), Vector3f(-0.5005f, -0.5005f, -0.5005f),
org.joml.AxisAngle4f(0f, 0f, 0f, 0f), org.joml.AxisAngle4f(0f, 0f, 0f, 1f),
Vector3f(1.01f, 1.01f, 1.01f), Vector3f(1.001f, 1.001f, 1.001f),
org.joml.AxisAngle4f(0f, 0f, 0f, 0f) org.joml.AxisAngle4f(0f, 0f, 0f, 1f)
) )
e.isGlowing = true e.isGlowing = true
e.glowColorOverride = Color.AQUA // 鮮やかな色 e.glowColorOverride = Config.veinMinerBlockColors[block.type] ?: block.blockData.getMapColor()
e.brightness = Display.Brightness(15, 15) // 最大輝度 e.brightness = Display.Brightness(15, 15) // 最大輝度
e.isVisibleByDefault = false e.isVisibleByDefault = false
} }
@ -128,6 +131,7 @@ class VeinMinerComponent(private val plugin: JavaPlugin) : ToolComponent {
} }
} }
activeHighlights[player.uniqueId] = HighlightSession(targetBlock, entities, System.currentTimeMillis()) activeHighlights[player.uniqueId] = HighlightSession(targetBlock, entities, System.currentTimeMillis())
} }
@ -162,44 +166,16 @@ class VeinMinerComponent(private val plugin: JavaPlugin) : ToolComponent {
val dropLocation = block.location.add(0.5, 0.5, 0.5) val dropLocation = block.location.add(0.5, 0.5, 0.5)
val hasSilkTouch = item.containsEnchantment(Enchantment.SILK_TOUCH) val hasSilkTouch = item.containsEnchantment(Enchantment.SILK_TOUCH)
val isDropItems = player.gameMode != GameMode.CREATIVE && player.gameMode != GameMode.SPECTATOR
var soundPlayed = false
for (target in blocksToBreak) { for (target in blocksToBreak) {
if (target == block) continue // 起点ブロックはイベントフローで破壊される if (target == block) continue // 起点ブロックはイベントフローで破壊される
// 保護プラグインチェック (擬似イベント) if (isDropItems){
val checkEvent = BlockBreakEvent(target, player) target.breakNaturally(item, true, true, true)
Bukkit.getPluginManager().callEvent(checkEvent) player.damageItemStack(item, 1)
if (checkEvent.isCancelled) continue
// 演出 (最初の1回だけ、または確率で)
if (!soundPlayed) {
target.world.playSound(target.location, target.blockSoundGroup.breakSound, 1f, 1f)
target.world.spawnParticle(org.bukkit.Particle.BLOCK, target.location.add(0.5,0.5,0.5), 10, 0.3, 0.3, 0.3, target.blockData)
soundPlayed = true
} }
else target.type = Material.AIR
// ドロップ処理
val drops = target.getDrops(item)
for (drop in drops) {
target.world.dropItem(dropLocation, drop)
}
// 経験値
if (!hasSilkTouch) {
val xp = getExpFromBlock(target.type)
if (xp > 0) {
val orb = target.world.spawn(dropLocation, ExperienceOrb::class.java)
orb.experience = xp
}
}
// ブロック消去
target.type = Material.AIR
// 耐久消費
damageItem(player, item)
} }
} }
@ -264,46 +240,40 @@ class VeinMinerComponent(private val plugin: JavaPlugin) : ToolComponent {
private fun isValidTarget(block: Block, item: ItemStack): Boolean { private fun isValidTarget(block: Block, item: ItemStack): Boolean {
if (block.getDrops(item).isEmpty()) return false if (block.getDrops(item).isEmpty()) return false
val itemName = item.type.key.toString()
val blockName = block.type.key.toString()
var isAllowed = false
for (category in Config.veinMinerToolCategories) { for (category in Config.veinMinerToolCategories) {
if (itemName.contains(category.toolPattern)) { if (matchesTool(item, category.tool)) {
for (pattern in category.allowedBlockPatterns) { for (blockPattern in category.blocks) {
if (blockName.contains(pattern)) { if (matchesBlock(block, blockPattern)) {
isAllowed = true return true
break
} }
} }
} }
if (isAllowed) break
} }
return isAllowed return false
} }
private fun damageItem(player: Player, item: ItemStack) { private fun matchesTool(item: ItemStack, criteria: String): Boolean {
val meta = item.itemMeta as? Damageable ?: return // Tag check (#namespace:key)
val unbreakingLevel = item.getEnchantmentLevel(Enchantment.UNBREAKING) if (criteria.startsWith("#")) {
val key = NamespacedKey.fromString(criteria.substring(1))
if (unbreakingLevel > 0) { return key != null && Bukkit.getTag(Tag.REGISTRY_ITEMS, key, Material::class.java)?.isTagged(item.type) == true
val random = Random()
if (random.nextInt(unbreakingLevel + 1) > 0) {
return
}
} }
val maxDamage = item.type.maxDurability // Exact Material Match
if (maxDamage > 0) { val targetMat = Material.matchMaterial(criteria)
val newDamage = meta.damage + 1 return targetMat == item.type
if (newDamage >= maxDamage) { }
item.amount = 0
player.world.playSound(player.location, org.bukkit.Sound.ENTITY_ITEM_BREAK, 1f, 1f) private fun matchesBlock(block: Block, criteria: String): Boolean {
} else { // Tag check (#namespace:key)
meta.damage = newDamage if (criteria.startsWith("#")) {
item.itemMeta = meta val key = NamespacedKey.fromString(criteria.substring(1)) ?: return false
} return Bukkit.getTag(Tag.REGISTRY_BLOCKS, key, Material::class.java)?.isTagged(block.type) == true
} }
// Exact Material Match
val targetMat = Material.matchMaterial(criteria)
return targetMat != null && block.type == targetMat
} }
private fun getExpFromBlock(type: Material): Int { private fun getExpFromBlock(type: Material): Int {

View File

@ -9,42 +9,61 @@ components:
vein_miner: vein_miner:
max_blocks: 64 max_blocks: 64
activation_mode: "SNEAK" # SNEAK, ALWAYS, STAND activation_mode: "SNEAK" # SNEAK, ALWAYS, STAND
block_colors:
"#minecraft:diamond_ores": "#5DECE5"
"#minecraft:iron_ores": "#D8AF93"
"#minecraft:gold_ores": "#FCEE4B"
"#minecraft:copper_ores": "#966756"
"#minecraft:coal_ores": "#363636"
"#minecraft:redstone_ores": "#FF0000"
"#minecraft:lapis_ores": "#1F61AE"
"#minecraft:emerald_ores": "#00D93A"
"minecraft:ancient_debr ": "#623A32"
"minecraft:nether_quartz_ore": "#EADFD4"
compatible_groups: compatible_groups:
- [ "minecraft:diamond_ore", "minecraft:deepslate_diamond_ore" ] - [ "#minecraft:diamond_ores" ]
- [ "minecraft:iron_ore", "minecraft:deepslate_iron_ore" ] - [ "#minecraft:iron_ores" ]
- [ "minecraft:gold_ore", "minecraft:deepslate_gold_ore" ] - [ "#minecraft:gold_ores" ]
- [ "minecraft:copper_ore", "minecraft:deepslate_copper_ore" ] - [ "#minecraft:copper_ores" ]
- [ "minecraft:coal_ore", "minecraft:deepslate_coal_ore" ] - [ "#minecraft:coal_ores" ]
- [ "minecraft:redstone_ore", "minecraft:deepslate_redstone_ore" ] - [ "#minecraft:redstone_ores" ]
- [ "minecraft:lapis_ore", "minecraft:deepslate_lapis_ore" ] - [ "#minecraft:lapis_ores" ]
- [ "minecraft:emerald_ore", "minecraft:deepslate_emerald_ore" ] - [ "#minecraft:emerald_ores" ]
tool_categories: tool_categories:
- tool_pattern: "_pickaxe" - tool: "#minecraft:pickaxes"
allowed_blocks: active_blocks:
- "_ore" - "#minecraft:coal_ores"
- "#minecraft:iron_ores"
- "#minecraft:copper_ores"
- "#minecraft:gold_ores"
- "#minecraft:redstone_ores"
- "#minecraft:lapis_ores"
- "#minecraft:diamond_ores"
- "#minecraft:emerald_ores"
- "minecraft:nether_quartz_ore"
- "minecraft:ancient_debris" - "minecraft:ancient_debris"
- "minecraft:amethyst_block" - "minecraft:amethyst_block"
- "minecraft:budding_amethyst" - "minecraft:budding_amethyst"
- "minecraft:obsidian" - "minecraft:obsidian"
- tool_pattern: "_axe" - tool: "#minecraft:axes"
allowed_blocks: active_blocks:
- "_log" - "#minecraft:logs"
- "_stem"
- "_hyphae"
- "minecraft:mangrove_roots" - "minecraft:mangrove_roots"
- "minecraft:bamboo_block" - "minecraft:bamboo_block"
- tool_pattern: "_shovel" - tool: "#minecraft:shovels"
allowed_blocks: active_blocks:
- "minecraft:clay" - "minecraft:clay"
- "minecraft:gravel" - "minecraft:gravel"
- "minecraft:soul_sand" - "minecraft:soul_sand"
- "minecraft:soul_soil" - "minecraft:soul_soil"
- "minecraft:mud" - "minecraft:mud"
- "minecraft:snow" - "minecraft:snow"
- tool_pattern: "_hoe" - "minecraft:snow_block"
allowed_blocks: - tool: "#minecraft:hoes"
- "_leaves" active_blocks:
- "#minecraft:leaves"
- "minecraft:nether_wart_block" - "minecraft:nether_wart_block"
- "minecraft:shroomlight" - "minecraft:shroomlight"
- "minecraft:hay_block" - "minecraft:hay_block"
- "#minecraft:wart_blocks"