diff --git a/src/main/java/xyz/nucleoid/stimuli/event/block/BlockTrampleEvent.java b/src/main/java/xyz/nucleoid/stimuli/event/block/BlockTrampleEvent.java
new file mode 100644
index 0000000..dfe594d
--- /dev/null
+++ b/src/main/java/xyz/nucleoid/stimuli/event/block/BlockTrampleEvent.java
@@ -0,0 +1,37 @@
+package xyz.nucleoid.stimuli.event.block;
+
+import net.minecraft.block.BlockState;
+import net.minecraft.entity.LivingEntity;
+import net.minecraft.server.world.ServerWorld;
+import net.minecraft.util.ActionResult;
+import net.minecraft.util.math.BlockPos;
+import xyz.nucleoid.stimuli.event.StimulusEvent;
+
+/**
+ * Called when any {@link LivingEntity} attempts to trample a block, such as farmland or turtle eggs.
+ *
+ *
Upon return:
+ *
+ * - {@link ActionResult#SUCCESS} cancels further processing and allows the trample.
+ *
- {@link ActionResult#FAIL} cancels further processing and cancels the trample.
+ *
- {@link ActionResult#PASS} moves on to the next listener.
+ *
+ * If all listeners return {@link ActionResult#PASS}, the trample succeeds.
+ */
+public interface BlockTrampleEvent {
+ StimulusEvent EVENT = StimulusEvent.create(BlockTrampleEvent.class, ctx -> (entity, world, pos, from, to) -> {
+ try {
+ for (var listener : ctx.getListeners()) {
+ var result = listener.onTrample(entity, world, pos, from, to);
+ if (result != ActionResult.PASS) {
+ return result;
+ }
+ }
+ } catch (Throwable t) {
+ ctx.handleException(t);
+ }
+ return ActionResult.PASS;
+ });
+
+ ActionResult onTrample(LivingEntity entity, ServerWorld world, BlockPos pos, BlockState from, BlockState to);
+}
diff --git a/src/main/java/xyz/nucleoid/stimuli/mixin/block/FarmlandBlockMixin.java b/src/main/java/xyz/nucleoid/stimuli/mixin/block/FarmlandBlockMixin.java
index e65cf05..3579098 100644
--- a/src/main/java/xyz/nucleoid/stimuli/mixin/block/FarmlandBlockMixin.java
+++ b/src/main/java/xyz/nucleoid/stimuli/mixin/block/FarmlandBlockMixin.java
@@ -1,8 +1,10 @@
package xyz.nucleoid.stimuli.mixin.block;
import net.minecraft.block.BlockState;
+import net.minecraft.block.Blocks;
import net.minecraft.block.FarmlandBlock;
import net.minecraft.entity.Entity;
+import net.minecraft.entity.LivingEntity;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.ActionResult;
@@ -14,16 +16,25 @@
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import xyz.nucleoid.stimuli.Stimuli;
import xyz.nucleoid.stimuli.event.block.BlockBreakEvent;
+import xyz.nucleoid.stimuli.event.block.BlockTrampleEvent;
@Mixin(FarmlandBlock.class)
public class FarmlandBlockMixin {
@Inject(method = "onLandedUpon", at = @At(value = "INVOKE", target = "Lnet/minecraft/block/FarmlandBlock;setToDirt(Lnet/minecraft/block/BlockState;Lnet/minecraft/world/World;Lnet/minecraft/util/math/BlockPos;)V", shift = At.Shift.BEFORE), cancellable = true)
private void breakFarmland(World world, BlockState state, BlockPos pos, Entity entity, float fallDistance, CallbackInfo ci) {
- if (world instanceof ServerWorld serverWorld && entity instanceof ServerPlayerEntity player) {
- try (var invokers = Stimuli.select().forEntityAt(player, pos)) {
- var result = invokers.get(BlockBreakEvent.EVENT).onBreak(player, serverWorld, pos);
- if (result == ActionResult.FAIL) {
+ if (world instanceof ServerWorld serverWorld && entity instanceof LivingEntity livingEntity) {
+ try (var invokers = Stimuli.select().forEntityAt(entity, pos)) {
+ var trampleResult = invokers.get(BlockTrampleEvent.EVENT).onTrample(livingEntity, serverWorld, pos, state, Blocks.DIRT.getDefaultState());
+ if (trampleResult == ActionResult.FAIL) {
ci.cancel();
+ return;
+ }
+
+ if (livingEntity instanceof ServerPlayerEntity player) {
+ var breakResult = invokers.get(BlockBreakEvent.EVENT).onBreak(player, serverWorld, pos);
+ if (breakResult == ActionResult.FAIL) {
+ ci.cancel();
+ }
}
}
}
diff --git a/src/main/java/xyz/nucleoid/stimuli/mixin/block/TurtleEggBlockMixin.java b/src/main/java/xyz/nucleoid/stimuli/mixin/block/TurtleEggBlockMixin.java
new file mode 100644
index 0000000..500f75b
--- /dev/null
+++ b/src/main/java/xyz/nucleoid/stimuli/mixin/block/TurtleEggBlockMixin.java
@@ -0,0 +1,40 @@
+package xyz.nucleoid.stimuli.mixin.block;
+
+import net.minecraft.block.BlockState;
+import net.minecraft.block.Blocks;
+import net.minecraft.block.TurtleEggBlock;
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.LivingEntity;
+import net.minecraft.server.world.ServerWorld;
+import net.minecraft.util.ActionResult;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.world.World;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+import xyz.nucleoid.stimuli.Stimuli;
+import xyz.nucleoid.stimuli.event.block.BlockTrampleEvent;
+
+@Mixin(TurtleEggBlock.class)
+public class TurtleEggBlockMixin {
+ @Inject(method = "tryBreakEgg", at = @At(value = "INVOKE", target = "Lnet/minecraft/block/TurtleEggBlock;breakEgg(Lnet/minecraft/world/World;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;)V", shift = At.Shift.BEFORE), cancellable = true)
+ private void trampleTurtleEgg(World world, BlockState from, BlockPos pos, Entity entity, int inverseChance, CallbackInfo ci) {
+ if (world instanceof ServerWorld serverWorld && entity instanceof LivingEntity livingEntity) {
+ BlockState to = Blocks.AIR.getDefaultState();
+ if (from.contains(TurtleEggBlock.EGGS)) {
+ int eggs = from.get(TurtleEggBlock.EGGS);
+ if (eggs > 1) {
+ to = from.with(TurtleEggBlock.EGGS, eggs - 1);
+ }
+ }
+
+ try (var invokers = Stimuli.select().forEntityAt(entity, pos)) {
+ var trampleResult = invokers.get(BlockTrampleEvent.EVENT).onTrample(livingEntity, serverWorld, pos, from, to);
+ if (trampleResult == ActionResult.FAIL) {
+ ci.cancel();
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/resources/stimuli.mixins.json b/src/main/resources/stimuli.mixins.json
index b299829..8bb5a5e 100644
--- a/src/main/resources/stimuli.mixins.json
+++ b/src/main/resources/stimuli.mixins.json
@@ -8,6 +8,7 @@
"block.BlockMixin",
"block.BucketItemMixin",
"block.FarmlandBlockMixin",
+ "block.TurtleEggBlockMixin",
"entity.LivingEntityMixin",
"player.ClientConnectionMixin",
"player.CraftingResultInventoryMixin",