diff --git a/core/common/src/main/kotlin/com/willfp/libreforge/Utils.kt b/core/common/src/main/kotlin/com/willfp/libreforge/Utils.kt index 6752ee87a..4a5993706 100644 --- a/core/common/src/main/kotlin/com/willfp/libreforge/Utils.kt +++ b/core/common/src/main/kotlin/com/willfp/libreforge/Utils.kt @@ -64,9 +64,30 @@ fun Collection.filterNotEmpty() = internal val ItemStack?.isEcoEmpty: Boolean get() = Items.isEmpty(this) +@Suppress("DEPRECATION") +@Deprecated("Use applyDamage with the removeItem function") fun ItemStack.applyDamage(damage: Int, player: Player?): Boolean { + return this.applyDamage(damage, player) { this.type = Material.AIR } +} + +fun ItemStack.applyDamage(damage: Int, player: Player?, removeItem: Runnable): Boolean { val meta = this.itemMeta as? Damageable ?: return false - meta.damage += damage + val unbreaking = meta.getEnchantLevel(Enchantment.UNBREAKING) + + // Calculate actual damage considering unbreaking + // Each damage point has a chance to be negated + var actualDamage = damage + if (unbreaking > 0) { + var damageNegated = 0 + repeat(damage) { + // Chance to negate this damage point: (unbreakingLevel) / (unbreakingLevel + 1) + if (Math.random() < (unbreaking.toDouble() / (unbreaking + 1))) + damageNegated++ + } + actualDamage = damage - damageNegated + } + + meta.damage += actualDamage if (meta.damage >= this.type.maxDurability) { meta.damage = this.type.maxDurability.toInt() this.itemMeta = meta @@ -74,8 +95,7 @@ fun ItemStack.applyDamage(damage: Int, player: Player?): Boolean { Bukkit.getPluginManager().callEvent(PlayerItemBreakEvent(player, this)) player.playSound(player.location, Sound.ENTITY_ITEM_BREAK, SoundCategory.BLOCKS, 1f, 1f) } - @Suppress("DEPRECATION") - this.type = Material.AIR + removeItem.run() } else { this.itemMeta = meta } diff --git a/core/common/src/main/kotlin/com/willfp/libreforge/effects/impl/EffectDamageArmor.kt b/core/common/src/main/kotlin/com/willfp/libreforge/effects/impl/EffectDamageArmor.kt index 361623a60..925d7214f 100644 --- a/core/common/src/main/kotlin/com/willfp/libreforge/effects/impl/EffectDamageArmor.kt +++ b/core/common/src/main/kotlin/com/willfp/libreforge/effects/impl/EffectDamageArmor.kt @@ -2,6 +2,7 @@ package com.willfp.libreforge.effects.impl import com.willfp.eco.core.config.interfaces.Config import com.willfp.libreforge.NoCompileData +import com.willfp.libreforge.applyDamage import com.willfp.libreforge.arguments import com.willfp.libreforge.effects.Effect import com.willfp.libreforge.enumValueOfOrNull @@ -10,13 +11,9 @@ import com.willfp.libreforge.triggers.TriggerData import com.willfp.libreforge.triggers.TriggerParameter import org.bukkit.Bukkit import org.bukkit.Material -import org.bukkit.Sound -import org.bukkit.SoundCategory import org.bukkit.entity.Player -import org.bukkit.event.player.PlayerItemBreakEvent import org.bukkit.event.player.PlayerItemDamageEvent import org.bukkit.inventory.EquipmentSlot -import org.bukkit.inventory.ItemStack import org.bukkit.inventory.meta.Damageable @@ -67,10 +64,10 @@ object EffectDamageArmor : Effect("damage_armor") { val event = PlayerItemDamageEvent(victim, item, damage) Bukkit.getPluginManager().callEvent(event) if (!event.isCancelled) { - applyDamage(item, event.damage, victim) + item.applyDamage(event.damage, victim) } } else { - applyDamage(item, damage, null) + item.applyDamage(damage, null) } } } else { @@ -83,10 +80,10 @@ object EffectDamageArmor : Effect("damage_armor") { val event = PlayerItemDamageEvent(victim, item, damage) Bukkit.getPluginManager().callEvent(event) if (!event.isCancelled) { - applyDamage(item, event.damage, victim) + item.applyDamage(event.damage, victim) } } else { - applyDamage(item, damage, null) + item.applyDamage(damage, null) } } } @@ -94,26 +91,4 @@ object EffectDamageArmor : Effect("damage_armor") { return true } - - private fun applyDamage(itemStack: ItemStack, amount: Int, player: Player?) { - val meta = itemStack.itemMeta as? Damageable ?: return - - meta.damage += amount - - if (meta.damage >= itemStack.type.maxDurability) { - meta.damage = itemStack.type.maxDurability.toInt() - - itemStack.itemMeta = meta - - if (player != null) { - Bukkit.getPluginManager().callEvent(PlayerItemBreakEvent(player, itemStack)) - player.playSound(player.location, Sound.ENTITY_ITEM_BREAK, SoundCategory.BLOCKS, 1f, 1f) - } - - @Suppress("DEPRECATION") - itemStack.type = Material.AIR - } else { - itemStack.itemMeta = meta - } - } } \ No newline at end of file diff --git a/core/common/src/main/kotlin/com/willfp/libreforge/effects/impl/EffectDrill.kt b/core/common/src/main/kotlin/com/willfp/libreforge/effects/impl/EffectDrill.kt index 5121af822..b38825ab6 100644 --- a/core/common/src/main/kotlin/com/willfp/libreforge/effects/impl/EffectDrill.kt +++ b/core/common/src/main/kotlin/com/willfp/libreforge/effects/impl/EffectDrill.kt @@ -3,13 +3,13 @@ package com.willfp.libreforge.effects.impl import com.willfp.eco.core.config.interfaces.Config import com.willfp.eco.core.integrations.antigrief.AntigriefManager import com.willfp.eco.util.VectorUtils -import com.willfp.eco.util.containsIgnoreCase import com.willfp.libreforge.NoCompileData import com.willfp.libreforge.arguments import com.willfp.libreforge.effects.templates.MineBlockEffect import com.willfp.libreforge.getIntFromExpression import com.willfp.libreforge.triggers.TriggerData import com.willfp.libreforge.triggers.TriggerParameter +import org.bukkit.Material import org.bukkit.block.Block @@ -25,46 +25,47 @@ object EffectDrill : MineBlockEffect("drill") { override fun onTrigger(config: Config, data: TriggerData, compileData: NoCompileData): Boolean { val block = data.block ?: data.location?.block ?: return false - val player = data.player ?: return false + val world = block.world val amount = config.getIntFromExpression("amount", data) - if (player.isSneaking && config.getBool("disable_on_sneak")) { + if (player.isSneaking && config.getBool("disable_on_sneak")) return false - } val whitelist = config.getStringsOrNull("whitelist") + ?.mapNotNull { Material.matchMaterial(it.uppercase()) }?.toSet() + + val blacklist = config.getStringsOrNull("blacklisted_blocks") + ?.mapNotNull { Material.matchMaterial(it.uppercase()) }?.toSet() val blocks = mutableSetOf() + val checkHardness = config.getBool("check_hardness") + for (i in 1..amount) { val simplified = VectorUtils.simplifyVector(player.location.direction.normalize()).multiply(i) - val toBreak = block.world.getBlockAt(block.location.clone().add(simplified)) + val toBreak = world.getBlockAt(block.location.clone().add(simplified)) - if (config.getStrings("blacklisted_blocks").containsIgnoreCase(toBreak.type.name)) { + if (toBreak.type == Material.AIR) continue - } - if (whitelist != null) { - if (!whitelist.containsIgnoreCase(toBreak.type.name)) { - continue - } - } + if (toBreak.type.hardness < 0) + continue + + if (!AntigriefManager.canBreakBlock(player, toBreak)) + continue - if (config.getBool("check_hardness")) { - if (toBreak.type.hardness > block.type.hardness) { + if (blacklist != null) + if (toBreak.type in blacklist) continue - } - } - if (!AntigriefManager.canBreakBlock(player, toBreak)) { - continue - } + if (whitelist != null) + if (toBreak.type !in whitelist) + continue - if (toBreak.type.hardness < 0) { + if (checkHardness && toBreak.type.hardness > block.type.hardness) continue - } blocks.add(toBreak) } diff --git a/core/common/src/main/kotlin/com/willfp/libreforge/effects/impl/EffectGlowNearbyBlocks.kt b/core/common/src/main/kotlin/com/willfp/libreforge/effects/impl/EffectGlowNearbyBlocks.kt index 36eaf5db7..840d1f4e7 100644 --- a/core/common/src/main/kotlin/com/willfp/libreforge/effects/impl/EffectGlowNearbyBlocks.kt +++ b/core/common/src/main/kotlin/com/willfp/libreforge/effects/impl/EffectGlowNearbyBlocks.kt @@ -3,6 +3,7 @@ package com.willfp.libreforge.effects.impl import com.willfp.eco.core.config.interfaces.Config +import com.willfp.eco.core.events.MultiBlockBreakEvent import com.willfp.eco.util.TeamUtils import com.willfp.libreforge.NoCompileData import com.willfp.libreforge.arguments @@ -133,4 +134,28 @@ object EffectGlowNearbyBlocks : Effect("glow_nearby_blocks") { shulker.remove() } } + + @EventHandler + fun onBreak(event: MultiBlockBreakEvent) { + for (block in event.blocks) { + if (!block.hasMetadata("gnb-uuid")) { + return + } + + val uuid = block.getMetadata("gnb-uuid").firstOrNull { + it.value() is UUID + }?.value() as? UUID ?: return + + Bukkit.getServer().getEntity(uuid)?.remove() + + for (shulker in block.location.world.getNearbyEntities( + block.location, + 2.0, + 2.0, + 2.0 + ) { it.hasMetadata("gnb-shulker") }) { + shulker.remove() + } + } + } } diff --git a/core/common/src/main/kotlin/com/willfp/libreforge/effects/impl/EffectMineRadius.kt b/core/common/src/main/kotlin/com/willfp/libreforge/effects/impl/EffectMineRadius.kt index a2293dea1..d4ca04554 100644 --- a/core/common/src/main/kotlin/com/willfp/libreforge/effects/impl/EffectMineRadius.kt +++ b/core/common/src/main/kotlin/com/willfp/libreforge/effects/impl/EffectMineRadius.kt @@ -2,7 +2,6 @@ package com.willfp.libreforge.effects.impl import com.willfp.eco.core.config.interfaces.Config import com.willfp.eco.core.integrations.antigrief.AntigriefManager -import com.willfp.eco.util.containsIgnoreCase import com.willfp.libreforge.NoCompileData import com.willfp.libreforge.arguments import com.willfp.libreforge.effects.templates.MineBlockEffect @@ -24,59 +23,56 @@ object EffectMineRadius : MineBlockEffect("mine_radius") { override fun onTrigger(config: Config, data: TriggerData, compileData: NoCompileData): Boolean { val block = data.block ?: data.location?.block ?: return false val player = data.player ?: return false + val world = block.world val radius = config.getIntFromExpression("radius", data) - if (player.isSneaking && config.getBool("disable_on_sneak")) { + if (player.isSneaking && config.getBool("disable_on_sneak")) return false - } val whitelist = config.getStringsOrNull("whitelist") + ?.mapNotNull { Material.matchMaterial(it.uppercase()) }?.toSet() + + val blacklist = config.getStringsOrNull("blacklisted_blocks") + ?.mapNotNull { Material.matchMaterial(it.uppercase()) }?.toSet() + + val blocks = mutableListOf() - val blocks = mutableSetOf() + val checkHardness = config.getBool("check_hardness") + + for (y in (-radius..radius)) { + val endY = block.y + y + if (endY !in world.minHeight..world.maxHeight) { + continue + } - for (x in (-radius..radius)) { - for (y in (-radius..radius)) { + for (x in (-radius..radius)) { for (z in (-radius..radius)) { if (x == 0 && y == 0 && z == 0) { continue } - val toBreak = block.world.getBlockAt( - block.location.clone().add(x.toDouble(), y.toDouble(), z.toDouble()) - ) + val toBreak = world.getBlockAt(block.x + x, block.y + y, block.z + z) - if (toBreak.location.blockY !in block.world.minHeight..block.world.maxHeight) { + if (toBreak.type == Material.AIR) continue - } - if (config.getStrings("blacklisted_blocks").containsIgnoreCase(toBreak.type.name)) { + if (toBreak.type.hardness < 0) continue - } - if (whitelist != null) { - if (!whitelist.containsIgnoreCase(toBreak.type.name)) { - continue - } - } + if (!AntigriefManager.canBreakBlock(player, toBreak)) + continue - if (config.getBoolOrNull("check_hardness") != false) { - if (toBreak.type.hardness < 0 || toBreak.type.hardness > block.type.hardness) { + if (blacklist != null) + if (toBreak.type in blacklist) continue - } - } - - if (toBreak.type.hardness < 0) { - continue - } - if (toBreak.type == Material.AIR) { - continue - } + if (whitelist != null) + if (toBreak.type !in whitelist) + continue - if (!AntigriefManager.canBreakBlock(player, toBreak)) { + if (checkHardness && toBreak.type.hardness > block.type.hardness) continue - } blocks.add(toBreak) } diff --git a/core/common/src/main/kotlin/com/willfp/libreforge/effects/impl/EffectMineRadiusOneDeep.kt b/core/common/src/main/kotlin/com/willfp/libreforge/effects/impl/EffectMineRadiusOneDeep.kt index 64becf271..5cbb6e624 100644 --- a/core/common/src/main/kotlin/com/willfp/libreforge/effects/impl/EffectMineRadiusOneDeep.kt +++ b/core/common/src/main/kotlin/com/willfp/libreforge/effects/impl/EffectMineRadiusOneDeep.kt @@ -2,7 +2,6 @@ package com.willfp.libreforge.effects.impl import com.willfp.eco.core.config.interfaces.Config import com.willfp.eco.core.integrations.antigrief.AntigriefManager -import com.willfp.eco.util.containsIgnoreCase import com.willfp.eco.util.simplify import com.willfp.libreforge.NoCompileData import com.willfp.libreforge.arguments @@ -26,21 +25,33 @@ object EffectMineRadiusOneDeep : MineBlockEffect("mine_radius_one override fun onTrigger(config: Config, data: TriggerData, compileData: NoCompileData): Boolean { val block = data.block ?: data.location?.block ?: return false val player = data.player ?: return false + val world = block.world val radius = config.getIntFromExpression("radius", data) - if (player.isSneaking && config.getBool("disable_on_sneak")) { + if (player.isSneaking && config.getBool("disable_on_sneak")) return false - } val whitelist = config.getStringsOrNull("whitelist") + ?.mapNotNull { Material.matchMaterial(it.uppercase()) }?.toSet() + + val blacklist = config.getStringsOrNull("blacklisted_blocks") + ?.mapNotNull { Material.matchMaterial(it.uppercase()) }?.toSet() + + val blocks = mutableListOf() - val blocks = mutableSetOf() + val checkHardness = config.getBool("check_hardness") + val noCorners = config.getBool("no_corners") val ignoreVector = player.location.direction.simplify() - for (x in (-radius..radius)) { - for (y in (-radius..radius)) { + for (y in (-radius..radius)) { + val endY = block.y + y + if (endY !in world.minHeight..world.maxHeight) { + continue + } + + for (x in (-radius..radius)) { for (z in (-radius..radius)) { // Jank if (ignoreVector.x != 0.0 && x != 0) { @@ -60,7 +71,7 @@ object EffectMineRadiusOneDeep : MineBlockEffect("mine_radius_one continue } - if (config.getBool("no_corners")) { + if (noCorners) { val atXCorner = abs(x) == radius val atYCorner = abs(y) == radius val atZCorner = abs(z) == radius @@ -73,41 +84,27 @@ object EffectMineRadiusOneDeep : MineBlockEffect("mine_radius_one } } - val toBreak = block.world.getBlockAt( - block.location.clone().add(x.toDouble(), y.toDouble(), z.toDouble()) - ) + val toBreak = world.getBlockAt(block.x + x, block.y + y, block.z + z) - if (toBreak.location.blockY !in block.world.minHeight..block.world.maxHeight) { + if (toBreak.type == Material.AIR) continue - } - if (config.getStrings("blacklisted_blocks").containsIgnoreCase(toBreak.type.name)) { + if (toBreak.type.hardness < 0) continue - } - if (whitelist != null) { - if (!whitelist.containsIgnoreCase(toBreak.type.name)) { - continue - } - } + if (!AntigriefManager.canBreakBlock(player, toBreak)) + continue - if (config.getBoolOrNull("check_hardness") != false) { - if (toBreak.type.hardness > block.type.hardness) { + if (blacklist != null) + if (toBreak.type in blacklist) continue - } - } - - if (toBreak.type.hardness < 0) { - continue - } - if (toBreak.type == Material.AIR) { - continue - } + if (whitelist != null) + if (toBreak.type !in whitelist) + continue - if (!AntigriefManager.canBreakBlock(player, toBreak)) { + if (checkHardness && toBreak.type.hardness > block.type.hardness) continue - } blocks.add(toBreak) } diff --git a/core/common/src/main/kotlin/com/willfp/libreforge/effects/impl/EffectReplaceNear.kt b/core/common/src/main/kotlin/com/willfp/libreforge/effects/impl/EffectReplaceNear.kt index c1b6b9f03..5564027de 100644 --- a/core/common/src/main/kotlin/com/willfp/libreforge/effects/impl/EffectReplaceNear.kt +++ b/core/common/src/main/kotlin/com/willfp/libreforge/effects/impl/EffectReplaceNear.kt @@ -1,6 +1,7 @@ package com.willfp.libreforge.effects.impl import com.willfp.eco.core.config.interfaces.Config +import com.willfp.eco.core.events.MultiBlockBreakEvent import com.willfp.eco.core.integrations.antigrief.AntigriefManager import com.willfp.eco.core.items.Items import com.willfp.eco.util.containsIgnoreCase @@ -87,7 +88,11 @@ object EffectReplaceNear : Effect("replace_near") { } } - if (!(AntigriefManager.canBreakBlock(player, toReplace) && AntigriefManager.canPlaceBlock(player, toReplace))) { + if (!(AntigriefManager.canBreakBlock(player, toReplace) && AntigriefManager.canPlaceBlock( + player, + toReplace + )) + ) { continue } @@ -125,4 +130,17 @@ object EffectReplaceNear : Effect("replace_near") { block.type = Material.AIR event.isCancelled = true } + + @EventHandler + fun onBreak(event: MultiBlockBreakEvent) { + for (block in event.blocks) { + if (!block.hasMetadata("rn-block")) { + return + } + + block.removeMetadata("rn-block", plugin) + block.type = Material.AIR + event.blocks.remove(block) + } + } } diff --git a/core/common/src/main/kotlin/com/willfp/libreforge/effects/impl/EffectReplantCrops.kt b/core/common/src/main/kotlin/com/willfp/libreforge/effects/impl/EffectReplantCrops.kt index cd1a8db5b..56ab5a24c 100644 --- a/core/common/src/main/kotlin/com/willfp/libreforge/effects/impl/EffectReplantCrops.kt +++ b/core/common/src/main/kotlin/com/willfp/libreforge/effects/impl/EffectReplantCrops.kt @@ -1,6 +1,7 @@ package com.willfp.libreforge.effects.impl import com.willfp.eco.core.config.interfaces.Config +import com.willfp.eco.core.events.MultiBlockBreakEvent import com.willfp.eco.core.integrations.antigrief.AntigriefManager import com.willfp.eco.core.map.listMap import com.willfp.libreforge.Dispatcher @@ -36,11 +37,13 @@ object EffectReplantCrops : Effect("replant_crops") { holder: ProvidedHolder, compileData: NoCompileData ) { - players[dispatcher.uuid].add(ReplantConfig( - identifiers.uuid, - config.getBool("consume_seeds"), - config.getBool("only_fully_grown") - )) + players[dispatcher.uuid].add( + ReplantConfig( + identifiers.uuid, + config.getBool("consume_seeds"), + config.getBool("only_fully_grown") + ) + ) } override fun onDisable(dispatcher: Dispatcher<*>, identifiers: Identifiers, holder: ProvidedHolder) { @@ -139,6 +142,96 @@ object EffectReplantCrops : Effect("replant_crops") { } } + @EventHandler(ignoreCancelled = true) + fun handle(event: MultiBlockBreakEvent) { + val player = event.player + + if (players[player.uniqueId].isEmpty()) { + return + } + + for (block in event.blocks) { + val type = block.type + + if (!AntigriefManager.canPlaceBlock(player, block)) { + return + } + + if (type in arrayOf( + Material.GLOW_BERRIES, + Material.SWEET_BERRY_BUSH, + Material.CACTUS, + Material.BAMBOO, + Material.CHORUS_FLOWER, + Material.SUGAR_CANE + ) + ) { + return + } + + val data = block.blockData + + if (data !is Ageable) { + return + } + + val playerConfigs = players[player.uniqueId] + val consumeSeeds = playerConfigs.any { it.consumeSeeds } + val onlyFullyGrown = playerConfigs.all { it.onlyFullyGrown } + + if (onlyFullyGrown && data.age != data.maximumAge) { + return + } + + if (consumeSeeds) { + val item = ItemStack( + when (type) { + Material.WHEAT -> Material.WHEAT_SEEDS + Material.POTATOES -> Material.POTATO + Material.CARROTS -> Material.CARROT + Material.BEETROOTS -> Material.BEETROOT_SEEDS + Material.COCOA -> Material.COCOA_BEANS + else -> type + } + ) + + val hasSeeds = player.inventory.removeItem(item).isEmpty() + + if (!hasSeeds) { + return + } + } + + if (data.age != data.maximumAge) { + if (onlyFullyGrown) { + return + } + + event.setDropItems(block, false) + } + + data.age = 0 + + plugin.scheduler.run { + block.type = type + block.blockData = data + + // Improves compatibility with other plugins. + Bukkit.getPluginManager().callEvent( + BlockPlaceEvent( + block, + block.state, + block.getRelative(BlockFace.DOWN), + player.inventory.itemInMainHand, + player, + true, + EquipmentSlot.HAND + ) + ) + } + } + } + private data class ReplantConfig( val uuid: UUID, val consumeSeeds: Boolean, diff --git a/core/common/src/main/kotlin/com/willfp/libreforge/effects/impl/EffectTelekinesis.kt b/core/common/src/main/kotlin/com/willfp/libreforge/effects/impl/EffectTelekinesis.kt index 62d2663df..d2a2a9a17 100644 --- a/core/common/src/main/kotlin/com/willfp/libreforge/effects/impl/EffectTelekinesis.kt +++ b/core/common/src/main/kotlin/com/willfp/libreforge/effects/impl/EffectTelekinesis.kt @@ -3,6 +3,7 @@ package com.willfp.libreforge.effects.impl import com.willfp.eco.core.config.interfaces.Config import com.willfp.eco.core.drops.DropQueue import com.willfp.eco.core.events.EntityDeathByEntityEvent +import com.willfp.eco.core.events.MultiBlockDropItemEvent import com.willfp.eco.core.integrations.antigrief.AntigriefManager import com.willfp.eco.core.map.listMap import com.willfp.eco.util.TelekinesisUtils @@ -54,7 +55,8 @@ object EffectTelekinesis : Effect("telekinesis") { val block = event.block if (!plugin.configYml.getBool("effects.telekinesis.always-process-blocks") - && !TelekinesisUtils.testPlayer(player)) { + && !TelekinesisUtils.testPlayer(player) + ) { return } @@ -104,6 +106,31 @@ object EffectTelekinesis : Effect("telekinesis") { event.expToDrop = 0 } + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + fun handle(event: MultiBlockDropItemEvent) { + val player = event.player + + for (block in event.blocks) { + if (!plugin.configYml.getBool("effects.telekinesis.always-process-blocks") + && !TelekinesisUtils.testPlayer(player) + ) { + return + } + + if (!AntigriefManager.canBreakBlock(player, block)) { + return + } + + val drops = event.getItems(block).map { it.itemStack } + event.getItems(block).clear() + + DropQueue(player) + .setLocation(block.location) + .addItems(drops) + .push() + } + } + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) fun handle(event: EntityDeathByEntityEvent) { val victim = event.victim @@ -126,6 +153,7 @@ object EffectTelekinesis : Effect("telekinesis") { if (!killer.isTamed || !allowTamedMobKills) return killer.owner as? Player } + else -> null } ?: return diff --git a/core/common/src/main/kotlin/com/willfp/libreforge/effects/templates/MineBlockEffect.kt b/core/common/src/main/kotlin/com/willfp/libreforge/effects/templates/MineBlockEffect.kt index 483077fcd..aa097ca59 100644 --- a/core/common/src/main/kotlin/com/willfp/libreforge/effects/templates/MineBlockEffect.kt +++ b/core/common/src/main/kotlin/com/willfp/libreforge/effects/templates/MineBlockEffect.kt @@ -1,14 +1,21 @@ package com.willfp.libreforge.effects.templates import com.willfp.eco.core.config.interfaces.Config +import com.willfp.eco.core.events.MultiBlockBreakEvent +import com.willfp.eco.core.events.MultiBlockDropItemEvent import com.willfp.eco.util.runExempted +import com.willfp.libreforge.applyDamage import com.willfp.libreforge.effects.Effect import com.willfp.libreforge.plugin import com.willfp.libreforge.triggers.TriggerData import com.willfp.libreforge.triggers.TriggerParameter +import org.bukkit.Bukkit import org.bukkit.Material import org.bukkit.block.Block +import org.bukkit.entity.Item import org.bukkit.entity.Player +import org.bukkit.event.block.BlockBreakEvent +import org.bukkit.event.block.BlockDropItemEvent abstract class MineBlockEffect(id: String) : Effect(id) { private val ignoreKey = "blockbreakevent-ignore" @@ -23,18 +30,79 @@ abstract class MineBlockEffect(id: String) : Effect(id) { } protected fun Player.breakBlocksSafely(blocks: Collection) { + val item = this.inventory.itemInMainHand + val useMultiBlocksEvents = plugin.configYml.getBool("effects.use-multiblock-events") + if (plugin.configYml.getBool("effects.use-setblock-break")) { blocks.forEach { it.type = Material.AIR } } else { this.runExempted { + val blockList = mutableMapOf() + for (block in blocks) { if (block.world != this.world) { continue } + var items = block.getDrops(item).map { + block.world.createEntity( + block.location.toCenterLocation(), + Item::class.java + ).apply { itemStack = it } + } + + if (!useMultiBlocksEvents) { + val blockBreak = BlockBreakEvent(block, this) + Bukkit.getPluginManager().callEvent(blockBreak) + if (blockBreak.isCancelled) + continue + + if (blockBreak.isDropItems) { + val blockDrop = BlockDropItemEvent(block, block.state, this, items) + + Bukkit.getPluginManager().callEvent(blockDrop) + if (blockDrop.isCancelled) + continue + + items = blockDrop.items + } + } + + blockList[block] = MultiBlockDropItemEvent.BlockStateAndItems(block.state, items) + } + + val multiBlockBreak = MultiBlockBreakEvent(this, blockList.keys) + + if (useMultiBlocksEvents) { + Bukkit.getPluginManager().callEvent(multiBlockBreak) + if (multiBlockBreak.isCancelled) + return@runExempted + } + + // blockList is probably mutated by the event above, so we put it after + val multiBlockDrop = MultiBlockDropItemEvent(this, blockList) + + if (useMultiBlocksEvents) { + Bukkit.getPluginManager().callEvent(multiBlockDrop) + if (multiBlockDrop.isCancelled) + return@runExempted + } + + val damageToApply = blockList.size + + val iter = blockList.iterator() + while (iter.hasNext()) { + val (block, entry) = iter.next() block.setMetadata(ignoreKey, plugin.createMetadataValue(true)) - this.breakBlock(block) + block.type = Material.AIR + if (multiBlockBreak.isDropItems(block)) + entry.items.forEach { it.spawnAt(block.location.toCenterLocation()) } block.removeMetadata(ignoreKey, plugin) + iter.remove() + } + + item.applyDamage(damageToApply, this) { + this.inventory.setItemInMainHand(item.withType(Material.AIR)) } } } diff --git a/core/common/src/main/kotlin/com/willfp/libreforge/triggers/event/EditableDropEvent.kt b/core/common/src/main/kotlin/com/willfp/libreforge/triggers/event/EditableDropEvent.kt index 98e02a68f..2d083d06f 100644 --- a/core/common/src/main/kotlin/com/willfp/libreforge/triggers/event/EditableDropEvent.kt +++ b/core/common/src/main/kotlin/com/willfp/libreforge/triggers/event/EditableDropEvent.kt @@ -1,5 +1,6 @@ package com.willfp.libreforge.triggers.event +import com.willfp.eco.core.events.MultiBlockDropItemEvent import com.willfp.eco.util.toSingletonList import org.bukkit.Location import org.bukkit.entity.Item @@ -141,9 +142,43 @@ class EditableBlockDropEvent( } } +class EditableMultiBlockDropEvent( + private val event: MultiBlockDropItemEvent +) : EditableDropEvent() { + private val modifiers = mutableListOf() + + override fun addModifier(modifier: DropModifier) { + modifiers += modifier + } + + override val originalItems: List + get() = event.blocks.flatMap { event.getItems(it) }.map { it.itemStack } + + override val items: List + get() = originalItems.map { modifiers.modify(it) } + + override val dropLocation: Location + get() = event.player.location + + override fun removeItem(item: ItemStack) { + event.blocks.map { event.getItems(it) } + .forEach { list -> + list.removeIf { it.itemStack == item } + } + } + + override fun isCancelled(): Boolean { + return event.isCancelled + } + + override fun setCancelled(p0: Boolean) { + event.isCancelled = p0 + } +} + class EditablePlayerDropEvent( private val event: PlayerDropItemEvent -): EditableDropEvent() { +) : EditableDropEvent() { private val modifiers = mutableListOf() override fun addModifier(modifier: DropModifier) { diff --git a/core/common/src/main/kotlin/com/willfp/libreforge/triggers/impl/TriggerBlockItemDrop.kt b/core/common/src/main/kotlin/com/willfp/libreforge/triggers/impl/TriggerBlockItemDrop.kt index 87d12030f..3b213b64b 100644 --- a/core/common/src/main/kotlin/com/willfp/libreforge/triggers/impl/TriggerBlockItemDrop.kt +++ b/core/common/src/main/kotlin/com/willfp/libreforge/triggers/impl/TriggerBlockItemDrop.kt @@ -1,6 +1,7 @@ package com.willfp.libreforge.triggers.impl import com.willfp.eco.core.drops.DropQueue +import com.willfp.eco.core.events.MultiBlockDropItemEvent import com.willfp.eco.core.integrations.antigrief.AntigriefManager import com.willfp.libreforge.filterNotEmpty import com.willfp.libreforge.toDispatcher @@ -8,6 +9,7 @@ import com.willfp.libreforge.triggers.Trigger import com.willfp.libreforge.triggers.TriggerData import com.willfp.libreforge.triggers.TriggerParameter import com.willfp.libreforge.triggers.event.EditableBlockDropEvent +import com.willfp.libreforge.triggers.event.EditableMultiBlockDropEvent import org.bukkit.GameMode import org.bukkit.Material import org.bukkit.block.Block @@ -53,7 +55,11 @@ object TriggerBlockItemDrop : Trigger("block_item_drop") { player.toDispatcher(), TriggerData( player = player, - block = BrokenBlock(block, event.blockState.type, event.blockState.blockData), // Fixes the type always being AIR + block = BrokenBlock( + block, + event.blockState.type, + event.blockState.blockData + ), // Fixes the type always being AIR location = block.location, event = editableEvent, item = null, @@ -75,11 +81,60 @@ object TriggerBlockItemDrop : Trigger("block_item_drop") { } } + @EventHandler( + ignoreCancelled = true, + priority = EventPriority.LOW + ) + fun handle(event: MultiBlockDropItemEvent) { + val player = event.player + + if (player.gameMode == GameMode.CREATIVE || player.gameMode == GameMode.SPECTATOR) { + return + } + + val editableEvent = EditableMultiBlockDropEvent(event) + + for (block in event.blocks) { + + if (event.getBlockState(block) is Container) { + return + } + + if (!AntigriefManager.canBreakBlock(player, block)) { + return + } + + val originalDrops = event.getItems(block).map { it.itemStack }.filterNotEmpty() + + this.dispatch( + player.toDispatcher(), + TriggerData( + player = player, + block = BrokenBlock( + block, + block.type, + block.blockData + ), // Fixes the type always being AIR + location = player.location, + event = editableEvent, + item = null, + value = originalDrops.sumOf { it.amount }.toDouble() + ) + ) + } + + val newDrops = editableEvent.items + + for ((i, entry) in event.blocks.flatMap { event.getItems(it) }.withIndex()) { + entry.itemStack = newDrops[i].item + } + } + private class BrokenBlock( private val block: Block, private val type: Material, private val data: BlockData - ): Block by block { + ) : Block by block { override fun getType() = type override fun getBlockData(): BlockData = data } diff --git a/core/common/src/main/kotlin/com/willfp/libreforge/triggers/impl/TriggerMineBlock.kt b/core/common/src/main/kotlin/com/willfp/libreforge/triggers/impl/TriggerMineBlock.kt index 7ac86fd8b..13bb257b7 100644 --- a/core/common/src/main/kotlin/com/willfp/libreforge/triggers/impl/TriggerMineBlock.kt +++ b/core/common/src/main/kotlin/com/willfp/libreforge/triggers/impl/TriggerMineBlock.kt @@ -1,5 +1,6 @@ package com.willfp.libreforge.triggers.impl +import com.willfp.eco.core.events.MultiBlockBreakEvent import com.willfp.eco.core.integrations.antigrief.AntigriefManager import com.willfp.libreforge.toDispatcher import com.willfp.libreforge.triggers.Trigger @@ -38,4 +39,26 @@ object TriggerMineBlock : Trigger("mine_block") { ) ) } + + @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) + fun handle(event: MultiBlockBreakEvent) { + val player = event.player + + for (block in event.blocks) { + if (!AntigriefManager.canBreakBlock(player, block)) { + return + } + + this.dispatch( + player.toDispatcher(), + TriggerData( + player = player, + block = block, + location = block.location, + event = event, + item = player.inventory.itemInMainHand + ) + ) + } + } } diff --git a/core/common/src/main/resources/config.yml b/core/common/src/main/resources/config.yml index ce2ade502..84aaedb86 100644 --- a/core/common/src/main/resources/config.yml +++ b/core/common/src/main/resources/config.yml @@ -118,6 +118,14 @@ effects: # Only set this option to true if you know what you're doing! use-setblock-break: false + # If this is set to true, effects like mine_radius, drill, etc. will use instead a single multiblock event. + # This **will** break compatibility with other plugins, but will also majorly improve performance under load. + # Setting this to true means that it will trigger mine_block and block_item_drop, but other plugins will not + # be able to interact with the event. + # Not compatible with use-setblock-break set to true. + # Only set this option to true if you know what you're doing! + use-multiblock-events: false + multiply_drops: # Some items can be duplicated if they are placed on other blocks, e.g. torches, lanterns, etc. # This option will prevent this from happening by blacklisting certain blocks from being multiplied.