From c24b0982df9646fe35fa8f6de4fcfe78ee29e128 Mon Sep 17 00:00:00 2001 From: BeDePaY Date: Tue, 10 Feb 2026 14:30:04 +0300 Subject: [PATCH] add chance drop --- .../destroy/SpawnerBreakListener.java | 70 ++++++++++++++----- .../destroy/SpawnerExplosionListener.java | 55 ++++++++++++++- core/src/main/resources/config.yml | 14 ++++ .../resources/language/DonutSMP/messages.yml | 12 +++- .../resources/language/de_DE/messages.yml | 12 +++- .../resources/language/en_US/messages.yml | 12 +++- .../resources/language/vi_VN/messages.yml | 12 +++- 7 files changed, 161 insertions(+), 26 deletions(-) diff --git a/core/src/main/java/github/nighter/smartspawner/spawner/interactions/destroy/SpawnerBreakListener.java b/core/src/main/java/github/nighter/smartspawner/spawner/interactions/destroy/SpawnerBreakListener.java index b14efc8f..c5bc1fcf 100644 --- a/core/src/main/java/github/nighter/smartspawner/spawner/interactions/destroy/SpawnerBreakListener.java +++ b/core/src/main/java/github/nighter/smartspawner/spawner/interactions/destroy/SpawnerBreakListener.java @@ -27,7 +27,9 @@ import org.bukkit.inventory.meta.Damageable; import org.bukkit.inventory.meta.ItemMeta; +import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ThreadLocalRandom; public class SpawnerBreakListener implements Listener { private static final int MAX_STACK_SIZE = 64; @@ -130,12 +132,27 @@ private void handleSmartSpawnerBreak(Block block, SpawnerData spawner, Player pl plugin.getSpawnerGuiViewManager().closeAllViewersInventory(spawner); - SpawnerBreakResult result = processDrops(player, location, spawner, player.isSneaking(), block); + double dropChance = plugin.getConfig().getDouble("spawner_break.drop_chance", 100.0); + boolean shouldDrop = player.getGameMode() == GameMode.CREATIVE || passesChanceCheck(dropChance); - if (result.isSuccess()) { + Map chancePlaceholders = new HashMap<>(); + chancePlaceholders.put("chance", String.format("%.1f", dropChance)); + + if (shouldDrop) { + SpawnerBreakResult result = processDrops(player, location, spawner, player.isSneaking(), block); + + if (result.isSuccess()) { + if (player.getGameMode() != GameMode.CREATIVE) { + reduceDurability(tool, player, result.getDurabilityLoss()); + } + messageService.sendMessage(player, "spawner_break_success", chancePlaceholders); + } + } else { + cleanupSpawner(block, spawner); if (player.getGameMode() != GameMode.CREATIVE) { - reduceDurability(tool, player, result.getDurabilityLoss()); + reduceDurability(tool, player, plugin.getConfig().getInt("spawner_break.durability_loss", 1)); } + messageService.sendMessage(player, "spawner_break_no_drop", chancePlaceholders); } } finally { locationLockManager.unlock(location); @@ -162,25 +179,38 @@ private void handleVanillaSpawnerBreak(Block block, CreatureSpawner creatureSpaw return; } - EntityType entityType = creatureSpawner.getSpawnedType(); - ItemStack spawnerItem; - if (plugin.getConfig().getBoolean("natural_spawner.convert_to_smart_spawner", false)) { - spawnerItem = spawnerItemFactory.createSmartSpawnerItem(entityType); - } else { - spawnerItem = spawnerItemFactory.createVanillaSpawnerItem(entityType); - } + double dropChance = plugin.getConfig().getDouble("spawner_break.drop_chance", 100.0); + boolean shouldDrop = player.getGameMode() == GameMode.CREATIVE || passesChanceCheck(dropChance); - boolean directToInventory = plugin.getConfig().getBoolean("spawner_break.direct_to_inventory", false); + Map chancePlaceholders = new HashMap<>(); + chancePlaceholders.put("chance", String.format("%.1f", dropChance)); World world = location.getWorld(); if (world != null) { - block.setType(Material.AIR); - - if (directToInventory) { - giveSpawnersToPlayer(player, 1, spawnerItem); - player.playSound(player.getLocation(), Sound.ENTITY_ITEM_PICKUP, 0.5f, 1.2f); + if (shouldDrop) { + EntityType entityType = creatureSpawner.getSpawnedType(); + ItemStack spawnerItem; + if (plugin.getConfig().getBoolean("natural_spawner.convert_to_smart_spawner", false)) { + spawnerItem = spawnerItemFactory.createSmartSpawnerItem(entityType); + } else { + spawnerItem = spawnerItemFactory.createVanillaSpawnerItem(entityType); + } + + boolean directToInventory = plugin.getConfig().getBoolean("spawner_break.direct_to_inventory", false); + + block.setType(Material.AIR); + + if (directToInventory) { + giveSpawnersToPlayer(player, 1, spawnerItem); + player.playSound(player.getLocation(), Sound.ENTITY_ITEM_PICKUP, 0.5f, 1.2f); + } else { + world.dropItemNaturally(location.toCenterLocation(), spawnerItem); + } + + messageService.sendMessage(player, "spawner_break_success", chancePlaceholders); } else { - world.dropItemNaturally(location.toCenterLocation(), spawnerItem); + block.setType(Material.AIR); + messageService.sendMessage(player, "spawner_break_no_drop", chancePlaceholders); } reduceDurability(tool, player, plugin.getConfig().getInt("spawner_break.durability_loss", 1)); @@ -370,6 +400,12 @@ public int getDurabilityLoss() { } } + private boolean passesChanceCheck(double chance) { + if (chance >= 100.0) return true; + if (chance <= 0.0) return false; + return ThreadLocalRandom.current().nextDouble(100.0) < chance; + } + @EventHandler public void onSpawnerDamage(BlockDamageEvent event) { Block block = event.getBlock(); diff --git a/core/src/main/java/github/nighter/smartspawner/spawner/interactions/destroy/SpawnerExplosionListener.java b/core/src/main/java/github/nighter/smartspawner/spawner/interactions/destroy/SpawnerExplosionListener.java index 5c5c4e49..0134500e 100644 --- a/core/src/main/java/github/nighter/smartspawner/spawner/interactions/destroy/SpawnerExplosionListener.java +++ b/core/src/main/java/github/nighter/smartspawner/spawner/interactions/destroy/SpawnerExplosionListener.java @@ -4,31 +4,39 @@ import github.nighter.smartspawner.api.events.SpawnerExplodeEvent; import github.nighter.smartspawner.extras.HopperHandler; import github.nighter.smartspawner.spawner.data.SpawnerManager; +import github.nighter.smartspawner.spawner.item.SpawnerItemFactory; import github.nighter.smartspawner.spawner.properties.SpawnerData; import github.nighter.smartspawner.spawner.data.SpawnerFileHandler; import org.bukkit.Bukkit; +import org.bukkit.Location; import org.bukkit.Material; +import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; +import org.bukkit.block.CreatureSpawner; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.entity.EntityExplodeEvent; import org.bukkit.event.block.BlockExplodeEvent; import org.bukkit.entity.EntityType; +import org.bukkit.inventory.ItemStack; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.ThreadLocalRandom; public class SpawnerExplosionListener implements Listener { private final SmartSpawner plugin; private final SpawnerManager spawnerManager; private final SpawnerFileHandler spawnerFileHandler; + private final SpawnerItemFactory spawnerItemFactory; private final HopperHandler hopperHandler; public SpawnerExplosionListener(SmartSpawner plugin) { this.plugin = plugin; this.spawnerManager = plugin.getSpawnerManager(); this.spawnerFileHandler = plugin.getSpawnerFileHandler(); + this.spawnerItemFactory = plugin.getSpawnerItemFactory(); this.hopperHandler = plugin.getHopperHandler(); } @@ -61,6 +69,10 @@ private void handleExplosion(EntityExplodeEvent event, List blockList) { e = new SpawnerExplodeEvent(null, spawnerData.getSpawnerLocation(), 1, false); } } else { + double explosionDropChance = plugin.getConfig().getDouble("spawner_properties.default.explosion_drop_chance", 0.0); + if (passesChanceCheck(explosionDropChance)) { + dropSmartSpawnerItem(block, spawnerData); + } spawnerData.getSpawnerStop().set(true); String spawnerId = spawnerData.getSpawnerId(); cleanupAssociatedHopper(block); @@ -74,9 +86,13 @@ private void handleExplosion(EntityExplodeEvent event, List blockList) { Bukkit.getPluginManager().callEvent(e); } } else { - // Allow vanilla spawners to be destroyed if (plugin.getConfig().getBoolean("natural_spawner.protect_from_explosions", false)) { blocksToRemove.add(block); + } else { + double naturalDropChance = plugin.getConfig().getDouble("natural_spawner.explosion_drop_chance", 0.0); + if (passesChanceCheck(naturalDropChance)) { + dropVanillaSpawnerItem(block); + } } } } else if (block.getType() == Material.RESPAWN_ANCHOR) { @@ -114,6 +130,43 @@ private boolean hasProtectedSpawnersNearby(Block anchorBlock) { return false; } + private boolean passesChanceCheck(double chance) { + if (chance >= 100.0) return true; + if (chance <= 0.0) return false; + return ThreadLocalRandom.current().nextDouble(100.0) < chance; + } + + private void dropSmartSpawnerItem(Block block, SpawnerData spawnerData) { + Location location = block.getLocation(); + World world = location.getWorld(); + if (world == null) return; + + ItemStack spawnerItem; + if (spawnerData.isItemSpawner()) { + spawnerItem = spawnerItemFactory.createItemSpawnerItem(spawnerData.getSpawnedItemMaterial()); + } else { + spawnerItem = spawnerItemFactory.createSmartSpawnerItem(spawnerData.getEntityType()); + } + world.dropItemNaturally(location.toCenterLocation(), spawnerItem); + } + + private void dropVanillaSpawnerItem(Block block) { + Location location = block.getLocation(); + World world = location.getWorld(); + if (world == null) return; + + if (block.getState(false) instanceof CreatureSpawner creatureSpawner) { + EntityType entityType = creatureSpawner.getSpawnedType(); + ItemStack spawnerItem; + if (plugin.getConfig().getBoolean("natural_spawner.convert_to_smart_spawner", false)) { + spawnerItem = spawnerItemFactory.createSmartSpawnerItem(entityType); + } else { + spawnerItem = spawnerItemFactory.createVanillaSpawnerItem(entityType); + } + world.dropItemNaturally(location.toCenterLocation(), spawnerItem); + } + } + private void cleanupAssociatedHopper(Block block) { Block blockBelow = block.getRelative(BlockFace.DOWN); if (blockBelow.getType() == Material.HOPPER && hopperHandler != null) { diff --git a/core/src/main/resources/config.yml b/core/src/main/resources/config.yml index 05354341..096d3c4e 100644 --- a/core/src/main/resources/config.yml +++ b/core/src/main/resources/config.yml @@ -42,6 +42,11 @@ spawner_properties: allow_exp_mending: true # Allow spawners to repair items with stored XP protect_from_explosions: true # Protect spawner blocks from explosion + # Chance (0-100%) for spawner to drop as item when destroyed by explosion + # Only applies when protect_from_explosions is false + # 100.0 = always drops, 50.0 = 50% chance, 0.0 = never drops (just destroyed) + explosion_drop_chance: 0.0 + #--------------------------------------------------- # Spawner Breaking Mechanics #--------------------------------------------------- @@ -62,6 +67,10 @@ spawner_break: # Durability impact on tools when breaking a spawner durability_loss: 1 # Number of durability points deducted + # Chance (0-100%) for spawner to drop when broken with valid tool + # 100.0 = always drops, 50.0 = 50% chance, 0.0 = never drops + drop_chance: 100.0 + # Enchantment Requirements for successful spawner collection silk_touch: required: true # Whether Silk Touch is needed to obtain spawners @@ -85,6 +94,11 @@ natural_spawner: # Whether natural spawner block will be protected from explosions protect_from_explosions: false + # Chance (0-100%) for natural spawner to drop as item when destroyed by explosion + # Only applies when protect_from_explosions is false + # 100.0 = always drops, 50.0 = 50% chance, 0.0 = never drops (just destroyed) + explosion_drop_chance: 0.0 + #--------------------------------------------------- # Economy Settings #--------------------------------------------------- diff --git a/core/src/main/resources/language/DonutSMP/messages.yml b/core/src/main/resources/language/DonutSMP/messages.yml index 027c1826..99c69c94 100644 --- a/core/src/main/resources/language/DonutSMP/messages.yml +++ b/core/src/main/resources/language/DonutSMP/messages.yml @@ -152,11 +152,11 @@ spawner_break_no_permission: sound: entity.villager.no spawner_break_silk_touch_required: - action_bar: "&#ff5252ꜱɪʟᴋ ᴛᴏᴜᴄʜ ʀᴇǫᴜɪʀᴇᴅ" + action_bar: "&#ff5252ᴛᴏ ᴍɪɴᴇ ᴀ ꜱᴘᴀᴡɴᴇʀ ʏᴏᴜ ɴᴇᴇᴅ ᴀ ᴘɪᴄᴋᴀxᴇ ᴡɪᴛʜ ꜱɪʟᴋ ᴛᴏᴜᴄʜ ᴇɴᴄʜᴀɴᴛᴍᴇɴᴛ" sound: item.shield.block spawner_break_required_tools: - action_bar: "&#ff5252ᴄᴀɴ'ᴛ ʙʀᴇᴀᴋ ꜱᴘᴀᴡɴᴇʀ ᴡɪᴛʜ ᴛʜɪꜱ ᴛᴏᴏʟ!" + action_bar: "&#ff5252ᴛᴏ ᴍɪɴᴇ ᴀ ꜱᴘᴀᴡɴᴇʀ ʏᴏᴜ ɴᴇᴇᴅ ᴀ ᴘɪᴄᴋᴀxᴇ ᴡɪᴛʜ ꜱɪʟᴋ ᴛᴏᴜᴄʜ ᴇɴᴄʜᴀɴᴛᴍᴇɴᴛ" sound: item.shield.block spawner_break_warning: @@ -165,6 +165,14 @@ spawner_break_warning: subtitle: "&#e6e6faɪᴛᴇᴍꜱ ɪɴ ꜱᴘᴀᴡɴᴇʀ ᴡɪʟʟ ʙᴇ ʟᴏꜱᴛ!" sound: block.note_block.bass +spawner_break_success: + action_bar: "%eb9aꜱᴘᴀᴡɴᴇʀ ꜱᴜᴄᴄᴇꜱꜱꜰᴜʟʟʏ ᴍɪɴᴇᴅ! &#f8f8ff(%eb9a{chance}% &#f8f8ffᴄʜᴀɴᴄᴇ)" + sound: entity.experience_orb.pickup + +spawner_break_no_drop: + action_bar: "&#ff5252ꜰᴀɪʟᴇᴅ ᴛᴏ ᴍɪɴᴇ ꜱᴘᴀᴡɴᴇʀ &#f8f8ff(&#ff5252{chance}% &#f8f8ffᴄʜᴀɴᴄᴇ)" + sound: block.note_block.bass + natural_spawner_break_blocked: action_bar: "&#ff5252ɴᴀᴛᴜʀᴀʟ ꜱᴘᴀᴡɴᴇʀꜱ ᴄᴀɴɴᴏᴛ ʙᴇ ʙʀᴏᴋᴇɴ ᴀɴᴅ ᴜꜱᴇᴅ" sound: block.note_block.pling diff --git a/core/src/main/resources/language/de_DE/messages.yml b/core/src/main/resources/language/de_DE/messages.yml index fa387e7e..454a6de9 100644 --- a/core/src/main/resources/language/de_DE/messages.yml +++ b/core/src/main/resources/language/de_DE/messages.yml @@ -152,11 +152,11 @@ spawner_break_no_permission: sound: entity.villager.no spawner_break_silk_touch_required: - action_bar: "&#ff5252ʙᴇʜᴜᴛꜱᴀᴍᴋᴇɪᴛ ᴇʀꜰᴏʀᴅᴇʀʟɪᴄʜ" + action_bar: "&#ff5252ᴢᴜᴍ ᴀʙʙᴀᴜᴇɴ ᴇɪɴᴇꜱ ꜱᴘᴀᴡɴᴇʀꜱ ʙʀᴀᴜᴄʜꜱᴛ ᴅᴜ ᴇɪɴᴇ ꜱᴘɪᴛᴢʜᴀᴄᴋᴇ ᴍɪᴛ ʙᴇʜᴜᴛꜱᴀᴍᴋᴇɪᴛ" sound: item.shield.block spawner_break_required_tools: - action_bar: "&#ff5252ꜱᴘᴀᴡɴᴇʀ ᴋᴀɴɴ ɴɪᴄʜᴛ ᴍɪᴛ ᴅɪᴇꜱᴇᴍ ᴡᴇʀᴋᴢᴇᴜɢ ᴀʙɢᴇʙᴀᴜᴛ ᴡᴇʀᴅᴇɴ!" + action_bar: "&#ff5252ᴢᴜᴍ ᴀʙʙᴀᴜᴇɴ ᴇɪɴᴇꜱ ꜱᴘᴀᴡɴᴇʀꜱ ʙʀᴀᴜᴄʜꜱᴛ ᴅᴜ ᴇɪɴᴇ ꜱᴘɪᴛᴢʜᴀᴄᴋᴇ ᴍɪᴛ ʙᴇʜᴜᴛꜱᴀᴍᴋᴇɪᴛ" sound: item.shield.block spawner_break_warning: @@ -165,6 +165,14 @@ spawner_break_warning: subtitle: "&#e6e6faɪᴛᴇᴍꜱ ɪᴍ ꜱᴘᴀᴡɴᴇʀ ɢᴇʜᴇɴ ᴠᴇʀʟᴏʀᴇɴ!" sound: block.note_block.bass +spawner_break_success: + action_bar: "%eb9aꜱᴘᴀᴡɴᴇʀ ᴇʀꜰᴏʟɢʀᴇɪᴄʜ ᴀʙɢᴇʙᴀᴜᴛ! &#f8f8ff(%eb9a{chance}% &#f8f8ffᴄʜᴀɴᴄᴇ)" + sound: entity.experience_orb.pickup + +spawner_break_no_drop: + action_bar: "&#ff5252ꜱᴘᴀᴡɴᴇʀ ᴀʙʙᴀᴜ ꜰᴇʜʟɢᴇꜱᴄʜʟᴀɢᴇɴ &#f8f8ff(&#ff5252{chance}% &#f8f8ffᴄʜᴀɴᴄᴇ)" + sound: block.note_block.bass + natural_spawner_break_blocked: action_bar: "&#ff5252ɴᴀᴛᴜ̈ʀʟɪᴄʜᴇ ꜱᴘᴀᴡɴᴇʀ ᴋᴏ̈ɴɴᴇɴ ɴɪᴄʜᴛ ᴀʙɢᴇʙᴀᴜᴛ ᴜɴᴅ ᴠᴇʀᴡᴇɴᴅᴇᴛ ᴡᴇʀᴅᴇɴ" sound: block.note_block.pling diff --git a/core/src/main/resources/language/en_US/messages.yml b/core/src/main/resources/language/en_US/messages.yml index c12ed30d..ec49f915 100644 --- a/core/src/main/resources/language/en_US/messages.yml +++ b/core/src/main/resources/language/en_US/messages.yml @@ -152,11 +152,11 @@ spawner_break_no_permission: sound: entity.villager.no spawner_break_silk_touch_required: - action_bar: "&#ff5252ꜱɪʟᴋ ᴛᴏᴜᴄʜ ʀᴇǫᴜɪʀᴇᴅ" + action_bar: "&#ff5252ᴛᴏ ᴍɪɴᴇ ᴀ ꜱᴘᴀᴡɴᴇʀ ʏᴏᴜ ɴᴇᴇᴅ ᴀ ᴘɪᴄᴋᴀxᴇ ᴡɪᴛʜ ꜱɪʟᴋ ᴛᴏᴜᴄʜ ᴇɴᴄʜᴀɴᴛᴍᴇɴᴛ" sound: item.shield.block spawner_break_required_tools: - action_bar: "&#ff5252ᴄᴀɴ'ᴛ ʙʀᴇᴀᴋ ꜱᴘᴀᴡɴᴇʀ ᴡɪᴛʜ ᴛʜɪꜱ ᴛᴏᴏʟ!" + action_bar: "&#ff5252ᴛᴏ ᴍɪɴᴇ ᴀ ꜱᴘᴀᴡɴᴇʀ ʏᴏᴜ ɴᴇᴇᴅ ᴀ ᴘɪᴄᴋᴀxᴇ ᴡɪᴛʜ ꜱɪʟᴋ ᴛᴏᴜᴄʜ ᴇɴᴄʜᴀɴᴛᴍᴇɴᴛ" sound: item.shield.block spawner_break_warning: @@ -165,6 +165,14 @@ spawner_break_warning: subtitle: "&#e6e6faɪᴛᴇᴍꜱ ɪɴ ꜱᴘᴀᴡɴᴇʀ ᴡɪʟʟ ʙᴇ ʟᴏꜱᴛ!" sound: block.note_block.bass +spawner_break_success: + action_bar: "%eb9aꜱᴘᴀᴡɴᴇʀ ꜱᴜᴄᴄᴇꜱꜱꜰᴜʟʟʏ ᴍɪɴᴇᴅ! &#f8f8ff(%eb9a{chance}% &#f8f8ffᴄʜᴀɴᴄᴇ)" + sound: entity.experience_orb.pickup + +spawner_break_no_drop: + action_bar: "&#ff5252ꜰᴀɪʟᴇᴅ ᴛᴏ ᴍɪɴᴇ ꜱᴘᴀᴡɴᴇʀ &#f8f8ff(&#ff5252{chance}% &#f8f8ffᴄʜᴀɴᴄᴇ)" + sound: block.note_block.bass + natural_spawner_break_blocked: action_bar: "&#ff5252ɴᴀᴛᴜʀᴀʟ ꜱᴘᴀᴡɴᴇʀꜱ ᴄᴀɴɴᴏᴛ ʙᴇ ʙʀᴏᴋᴇɴ ᴀɴᴅ ᴜꜱᴇᴅ" sound: block.note_block.pling diff --git a/core/src/main/resources/language/vi_VN/messages.yml b/core/src/main/resources/language/vi_VN/messages.yml index 4a284da5..f9aef60d 100644 --- a/core/src/main/resources/language/vi_VN/messages.yml +++ b/core/src/main/resources/language/vi_VN/messages.yml @@ -155,11 +155,11 @@ spawner_break_no_permission: sound: entity.villager.no spawner_break_silk_touch_required: - action_bar: "&#ff5252ᴄầɴ ᴘʜù ᴘʜéᴘ sɪʟᴋ ᴛᴏᴜᴄʜ để đậᴘ" + action_bar: "&#ff5252để đậᴘ ꜱᴘᴀᴡɴᴇʀ ᴄầɴ ᴄúᴘ ᴄó ᴘʜù ᴘʜéᴘ sɪʟᴋ ᴛᴏᴜᴄʜ" sound: item.shield.block spawner_break_required_tools: - action_bar: "&#ff5252ᴋʜôɴɢ ᴛʜể ᴘʜá ꜱᴘᴀᴡɴᴇʀ ʙằɴɢ ᴄôɴɢ ᴄụ ɴàʏ!!" + action_bar: "&#ff5252để đậᴘ ꜱᴘᴀᴡɴᴇʀ ᴄầɴ ᴄúᴘ ᴄó ᴘʜù ᴘʜéᴘ sɪʟᴋ ᴛᴏᴜᴄʜ" sound: item.shield.block spawner_break_warning: @@ -168,6 +168,14 @@ spawner_break_warning: subtitle: "&#e6e6faᴛấᴛ ᴄả ᴠậᴛ ᴘʜẩᴍ ᴛʀᴏɴɢ ꜱᴘᴀᴡɴᴇʀ sẽ ʙị ᴍấᴛ!" sound: block.note_block.bass +spawner_break_success: + action_bar: "%eb9ađã đậᴘ ᴛʜàɴʜ ᴄôɴɢ ꜱᴘᴀᴡɴᴇʀ! &#f8f8ff(%eb9a{chance}% &#f8f8ffᴛỉ ʟệ)" + sound: entity.experience_orb.pickup + +spawner_break_no_drop: + action_bar: "&#ff5252đậᴘ ꜱᴘᴀᴡɴᴇʀ ᴛʜấᴛ ʙạɪ &#f8f8ff(&#ff5252{chance}% &#f8f8ffᴛỉ ʟệ)" + sound: block.note_block.bass + natural_spawner_break_blocked: action_bar: "&#ff5252ꜱᴘᴀᴡɴᴇʀ ᴛự ɴʜɪêɴ ᴋʜôɴɢ ᴛʜể ʙị ᴘʜá và sử ᴅụɴɢ" sound: block.note_block.pling