From e343c4eb445d66791d6c23446d466a1a2a4941eb Mon Sep 17 00:00:00 2001
From: SamB440 <sam@islandearth.net>
Date: Sat, 11 Feb 2023 15:41:06 +0000
Subject: [PATCH] Add projectile hit simulation API (#8816)

This adds API to force a projectile to hit a provided entity. Example usage could be if you have a player disguised as another entity, you could simulate an arrow colliding with the (fake) entity hitbox.
---
 patches/api/More-Projectile-API.patch         | 35 +++++++++++++++++++
 ...rojectileHitEvent-for-piercing-arrow.patch |  2 +-
 patches/server/More-Projectile-API.patch      | 17 +++++++++
 3 files changed, 53 insertions(+), 1 deletion(-)

diff --git a/patches/api/More-Projectile-API.patch b/patches/api/More-Projectile-API.patch
index b45037da6a..d944bdf8c5 100644
--- a/patches/api/More-Projectile-API.patch
+++ b/patches/api/More-Projectile-API.patch
@@ -225,6 +225,41 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +     * @param beenShot has been in shot into the world
 +     */
 +    void setHasBeenShot(boolean beenShot);
++
++    /**
++     * Gets whether this projectile can hit an entity.
++     * <p>
++     * This method returns true under the following conditions:
++     * <p>
++     * - The shooter can see the entity ({@link Player#canSee(Entity)}) <p>
++     * - The entity is alive and not a spectator <p>
++     * - The projectile has left the hitbox of the shooter ({@link #hasLeftShooter()})<p>
++     * - If this is an arrow with piercing, it has not pierced the entity already
++     *
++     * @param entity the entity to check if this projectile can hit
++     * @return true if this projectile can damage the entity, false otherwise
++     */
++    boolean canHitEntity(@org.jetbrains.annotations.NotNull Entity entity);
++
++    /**
++     * Makes this projectile hit a specific entity.
++     * This uses the current position of the projectile for the hit point.
++     * Using this method will result in {@link org.bukkit.event.entity.ProjectileHitEvent} being called.
++     * @param entity the entity to hit
++     * @see #hitEntity(Entity, org.bukkit.util.Vector)
++     * @see #canHitEntity(Entity) 
++     */
++    void hitEntity(@org.jetbrains.annotations.NotNull Entity entity);
++
++    /**
++     * Makes this projectile hit a specific entity from a specific point.
++     * Using this method will result in {@link org.bukkit.event.entity.ProjectileHitEvent} being called.
++     * @param entity the entity to hit
++     * @param vector the direction to hit from
++     * @see #hitEntity(Entity)
++     * @see #canHitEntity(Entity) 
++     */
++    void hitEntity(@org.jetbrains.annotations.NotNull Entity entity, @org.jetbrains.annotations.NotNull org.bukkit.util.Vector vector);
 +    // Paper end
  }
 diff --git a/src/main/java/org/bukkit/entity/ShulkerBullet.java b/src/main/java/org/bukkit/entity/ShulkerBullet.java
diff --git a/patches/server/Fix-cancelling-ProjectileHitEvent-for-piercing-arrow.patch b/patches/server/Fix-cancelling-ProjectileHitEvent-for-piercing-arrow.patch
index 8805b11086..9da51bd837 100644
--- a/patches/server/Fix-cancelling-ProjectileHitEvent-for-piercing-arrow.patch
+++ b/patches/server/Fix-cancelling-ProjectileHitEvent-for-piercing-arrow.patch
@@ -24,7 +24,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
  
 +    // Paper start
 +    @Override
-+    protected void preOnHit(HitResult hitResult) {
++    public void preOnHit(HitResult hitResult) {
 +        super.preOnHit(hitResult);
 +        if (hitResult instanceof EntityHitResult entityHitResult && this.hitCancelled && this.getPierceLevel() > 0) {
 +            if (this.piercingIgnoreEntityIds == null) {
diff --git a/patches/server/More-Projectile-API.patch b/patches/server/More-Projectile-API.patch
index 207fcb2f15..a30f72f3a7 100644
--- a/patches/server/More-Projectile-API.patch
+++ b/patches/server/More-Projectile-API.patch
@@ -14,6 +14,8 @@ public net.minecraft.world.entity.projectile.AbstractArrow soundEvent
 public net.minecraft.world.entity.projectile.ThrownTrident dealtDamage
 public net.minecraft.world.entity.projectile.Projectile hasBeenShot
 public net.minecraft.world.entity.projectile.Projectile leftOwner
+public net.minecraft.world.entity.projectile.Projectile preOnHit(Lnet/minecraft/world/phys/HitResult;)V
+public net.minecraft.world.entity.projectile.Projectile canHitEntity(Lnet/minecraft/world/entity/Entity;)Z
 
 Co-authored-by: Nassim Jahnke <nassim@njahnke.dev>
 
@@ -72,6 +74,21 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
 +    }
 +
 +    @Override
++    public boolean canHitEntity(org.bukkit.entity.Entity entity) {
++        return this.getHandle().canHitEntity(((CraftEntity) entity).getHandle());
++    }
++
++    @Override
++    public void hitEntity(org.bukkit.entity.Entity entity) {
++        this.getHandle().preOnHit(new net.minecraft.world.phys.EntityHitResult(((CraftEntity) entity).getHandle()));
++    }
++
++    @Override
++    public void hitEntity(org.bukkit.entity.Entity entity, org.bukkit.util.Vector vector) {
++        this.getHandle().preOnHit(new net.minecraft.world.phys.EntityHitResult(((CraftEntity) entity).getHandle(), new net.minecraft.world.phys.Vec3(vector.getX(), vector.getY(), vector.getZ())));
++    }
++
++    @Override
 +    public net.minecraft.world.entity.projectile.Projectile getHandle() {
 +        return (net.minecraft.world.entity.projectile.Projectile) entity;
 +    }