diff --git a/src/main/java/dev/draylar/magna/mixin/AbstractBlockStateMixin.java b/src/main/java/dev/draylar/magna/mixin/AbstractBlockStateMixin.java
new file mode 100644
index 0000000..9fd5e81
--- /dev/null
+++ b/src/main/java/dev/draylar/magna/mixin/AbstractBlockStateMixin.java
@@ -0,0 +1,62 @@
+package dev.draylar.magna.mixin;
+
+import dev.draylar.magna.api.MagnaTool;
+import net.minecraft.block.AbstractBlock;
+import net.minecraft.block.BlockState;
+import net.minecraft.entity.player.PlayerEntity;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.world.BlockView;
+import net.minecraft.world.World;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Unique;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
+
+import java.util.List;
+
+/**
+ * A mixin that calculates the block breaking delta for {@link MagnaTool MagnaTool}s.
+ */
+@Mixin(AbstractBlock.AbstractBlockState.class)
+public class AbstractBlockStateMixin {
+ /**
+ * Compute the block breaking delta for {@link MagnaTool}s. If the held item is not a MagnaTool, this mixin
+ * does nothing.
+ *
+ *
The delta is computed by taking the minimum delta of the affected blocks. Mining speed is effectively limited
+ * by the hardest block being broken.
+ */
+ @Inject(
+ method = "calcBlockBreakingDelta",
+ at = @At("HEAD"),
+ cancellable = true
+ )
+ public void calcBlockBreakingDelta(PlayerEntity player, BlockView world, BlockPos pos, CallbackInfoReturnable cir) {
+ ItemStack stack = player.getInventory().getMainHandStack();
+ if (stack.getItem() instanceof MagnaTool tool) {
+ cir.cancel();
+ int radius = tool.getRadius(stack);
+ cir.setReturnValue(calculateDelta(tool, player, radius));
+ }
+ }
+
+ @Unique
+ private static float calculateDelta(final MagnaTool tool, final PlayerEntity player, final int radius) {
+ World world = player.getWorld();
+
+ // Even though we already have the BlockPosition that the player is targeting, the side information wasn't
+ // passed along, which is necessary for figuring out how to expand the radius. Just throw that away and
+ // recompute.
+ List blocks = tool.getBlockFinder().findPositions(world, player, radius);
+
+ return blocks.stream()
+ .map(pos -> {
+ BlockState state = world.getBlockState(pos);
+ //noinspection deprecation
+ return state.getBlock().calcBlockBreakingDelta(state, player, world, pos);
+ })
+ .min(Float::compare).orElseThrow();
+ }
+}
diff --git a/src/main/resources/magna.mixins.json b/src/main/resources/magna.mixins.json
index 833c6a8..be088b4 100644
--- a/src/main/resources/magna.mixins.json
+++ b/src/main/resources/magna.mixins.json
@@ -3,6 +3,7 @@
"package": "dev.draylar.magna.mixin",
"compatibilityLevel": "JAVA_8",
"mixins": [
+ "AbstractBlockStateMixin",
"BlockMixin",
"PlayerInventoryMixin",
"ServerPlayerInteractionManagerMixin",
diff --git a/src/testmod/java/dev/draylar/magna/test/MagnaTest.java b/src/testmod/java/dev/draylar/magna/test/MagnaTest.java
index b190af5..286a582 100644
--- a/src/testmod/java/dev/draylar/magna/test/MagnaTest.java
+++ b/src/testmod/java/dev/draylar/magna/test/MagnaTest.java
@@ -4,6 +4,7 @@
import dev.draylar.magna.item.ExcavatorItem;
import dev.draylar.magna.item.HammerItem;
import net.fabricmc.api.ModInitializer;
+import net.minecraft.block.BlockState;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
@@ -31,6 +32,18 @@ public void onInitialize() {
new HammerItem(ToolMaterials.DIAMOND, 0, 0, new Item.Settings())
);
+ // Standard hammer with a particularly slow mining speed. Useful for testing break speed.
+ Registry.register(
+ Registries.ITEM,
+ new Identifier("magna", "hammer_slow_test"),
+ new HammerItem(ToolMaterials.DIAMOND, 0, 0, new Item.Settings()) {
+ @Override
+ public float getMiningSpeedMultiplier(ItemStack stack, BlockState state) {
+ return 1.0f;
+ }
+ }
+ );
+
// Standard Hammer with a tool material of Diamond and a modified depth.
Registry.register(
Registries.ITEM,