package com.minelittlepony.unicopia.projectile; import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.entity.Living; import com.minelittlepony.unicopia.mixin.MixinPersistentProjectileEntity; import net.minecraft.entity.Entity; import net.minecraft.entity.projectile.ProjectileEntity; import net.minecraft.util.math.Vec3d; public interface ProjectileUtil { /** * Checks if the given entity is a projectile. */ static boolean isProjectile(Entity e) { return e instanceof ProjectileEntity; } /** * Checks if the given entity is a projectile that is not stuck in the ground. */ static boolean isFlyingProjectile(Entity e) { return isProjectile(e) && !(e instanceof MixinPersistentProjectileEntity m && m.isInGround()); } /** * Checks if the given projectile was thrown by the given entity */ static boolean isProjectileThrownBy(Entity throwable, @Nullable T e) { return e != null && isProjectile(throwable) && e.equals(((ProjectileEntity) throwable).getOwner()); } /** * Sets the velocity and heading for a projectile. * * @param throwable The projectile * @param heading The directional heaving vector * @param velocity Velocity * @param inaccuracy Inaccuracy * @return True the projectile's heading was set, false otherwise */ static void setThrowableHeading(Entity throwable, Vec3d heading, float velocity, float inaccuracy) { if (throwable instanceof ProjectileEntity p) { p.setVelocity(heading.x, heading.y, heading.z, velocity, inaccuracy); } else { heading = heading.normalize().multiply(velocity); Vec3d vel = throwable.getVelocity(); throwable.addVelocity(heading.x - vel.x, heading.y - vel.y, heading.z - vel.z); Living.updateVelocity(throwable); } } /** * Reverses a projectile's direction to deflect it off a surface. * * @param projectile The projectile to deflect * @param post The position the projectile must be deflected away from * @param absorbtionRate Percentage of the projectile velocity to absorb. */ static void ricochet(Entity projectile, Vec3d pos, float absorbtionRate) { Vec3d position = projectile.getPos(); Vec3d motion = projectile.getVelocity(); Vec3d normal = position.subtract(pos).normalize(); Vec3d approach = motion.subtract(normal); if (approach.length() < motion.length()) { normal = normal.multiply(-1); } setThrowableHeading(projectile, normal, (float)motion.length() * absorbtionRate, 0); } }