diff --git a/patches/net/minecraft/world/level/Explosion.java.patch b/patches/net/minecraft/world/level/Explosion.java.patch index 1ed4425e85..eb8965677c 100644 --- a/patches/net/minecraft/world/level/Explosion.java.patch +++ b/patches/net/minecraft/world/level/Explosion.java.patch @@ -8,3 +8,11 @@ Vec3 vec3 = new Vec3(this.x, this.y, this.z); for (Entity entity : list) { +@@ -298,6 +_,7 @@ + d7 *= d10; + d9 *= d10; + Vec3 vec31 = new Vec3(d5, d7, d9); ++ vec31 = net.neoforged.neoforge.event.EventHooks.getExplosionKnockback(this.level, this, entity, vec31); + entity.setDeltaMovement(entity.getDeltaMovement().add(vec31)); + if (entity instanceof Player) { + Player player = (Player)entity; diff --git a/src/main/java/net/neoforged/neoforge/event/EventHooks.java b/src/main/java/net/neoforged/neoforge/event/EventHooks.java index 84b4850fa1..5baf9184c4 100644 --- a/src/main/java/net/neoforged/neoforge/event/EventHooks.java +++ b/src/main/java/net/neoforged/neoforge/event/EventHooks.java @@ -160,6 +160,7 @@ import net.neoforged.neoforge.event.level.ChunkTicketLevelUpdatedEvent; import net.neoforged.neoforge.event.level.ChunkWatchEvent; import net.neoforged.neoforge.event.level.ExplosionEvent; +import net.neoforged.neoforge.event.level.ExplosionKnockbackEvent; import net.neoforged.neoforge.event.level.LevelEvent; import net.neoforged.neoforge.event.level.PistonEvent; import net.neoforged.neoforge.event.level.SleepFinishedTimeEvent; @@ -594,6 +595,22 @@ public static void onExplosionDetonate(Level level, Explosion explosion, List + *
+ * This event is not {@link ICancellableEvent}.
+ * This event does not use {@link HasResult}.
+ * This event is fired on the {@link NeoForge#EVENT_BUS}.
+ */ +public class ExplosionKnockbackEvent extends ExplosionEvent { + private final Entity entity; + private Vec3 knockbackVelocity; + + public ExplosionKnockbackEvent(Level level, Explosion explosion, Entity entity, Vec3 knockbackVelocity) { + super(level, explosion); + this.entity = entity; + this.knockbackVelocity = knockbackVelocity; + } + + /** return the list of blocks affected by the explosion. */ + public List getAffectedBlocks() { + return getExplosion().getToBlow(); + } + + /** return the entity affected by the explosion knockback. */ + public Entity getAffectedEntity() { + return entity; + } + + /** return the explosion knockback velocity to apply to entity. */ + public Vec3 getKnockbackVelocity() { + return knockbackVelocity; + } + + /** Sets the explosion knockback velocity to add to the entity's existing velocity. */ + public void setKnockbackVelocity(Vec3 newKnockbackVelocity) { + this.knockbackVelocity = newKnockbackVelocity; + } +} diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/entity/EntityEventTests.java b/tests/src/main/java/net/neoforged/neoforge/debug/entity/EntityEventTests.java index d967be09ad..afc49b3892 100644 --- a/tests/src/main/java/net/neoforged/neoforge/debug/entity/EntityEventTests.java +++ b/tests/src/main/java/net/neoforged/neoforge/debug/entity/EntityEventTests.java @@ -5,6 +5,7 @@ package net.neoforged.neoforge.debug.entity; +import net.minecraft.core.BlockPos; import net.minecraft.core.registries.Registries; import net.minecraft.gametest.framework.GameTest; import net.minecraft.world.entity.EntityType; @@ -13,8 +14,12 @@ import net.minecraft.world.entity.animal.Pig; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.phys.Vec3; import net.neoforged.neoforge.event.entity.EntityAttributeModificationEvent; import net.neoforged.neoforge.event.entity.EntityTeleportEvent; +import net.neoforged.neoforge.event.level.ExplosionKnockbackEvent; import net.neoforged.testframework.DynamicTest; import net.neoforged.testframework.annotation.ForEachTest; import net.neoforged.testframework.annotation.TestHolder; @@ -69,4 +74,23 @@ static void entityAttributeModificationEvent(final DynamicTest test, final Regis donkey, d -> d.getAttribute(testAttr).getValue(), "test attribute", 1.5D)) .thenSucceed()); } + + @GameTest + @EmptyTemplate(value = "15x5x15", floor = true) + @TestHolder(description = "Tests if the pig only gets vertical knockback from explosion knockback event") + static void entityVerticalExplosionKnockbackEvent(final DynamicTest test) { + test.eventListeners().forge().addListener((final ExplosionKnockbackEvent event) -> { + if (event.getAffectedEntity() instanceof Pig) { + event.setKnockbackVelocity(new Vec3(0, event.getKnockbackVelocity().y(), 0)); + } + }); + + test.onGameTest(helper -> helper.startSequence(() -> helper.spawnWithNoFreeWill(EntityType.PIG, 8, 3, 7)) + .thenExecute(pig -> helper.setBlock(8, 2, 7, Blocks.ACACIA_LEAVES)) + .thenExecute(pig -> helper.getLevel().explode(null, helper.getLevel().damageSources().generic(), null, helper.absolutePos(new BlockPos(7, 2, 7)).getCenter(), 2f, false, Level.ExplosionInteraction.BLOW)) + .thenExecute(pig -> helper.assertEntityProperty(pig, p -> pig.getDeltaMovement().x() == 0 && pig.getDeltaMovement().y() != 0 && pig.getDeltaMovement().z() == 0, "Check explosion Knockback")) + .thenIdle(10) + .thenExecute(helper::killAllEntities) + .thenSucceed()); + } }