match vanilla arrow/trident orientation incl. arrow impact shake, add projectile test scenes
This commit is contained in:
1 parent
addebffba1
commit
fbea9dfe08
3 files changed
+154
-3
No files matched your search
@@ -134,6 +134,9 @@ public final class CemBaker implements EntityBaker<EntityState> {
|
||||
place = Affine.translation(s.x(), s.y(), s.z())
|
||||
.mul(Affine.scale(extra))
|
||||
.mul(Affine.rotY(yawSign * Math.toRadians(s.bodyYaw())));
|
||||
// Projectiles (verified against ArrowRenderer/ThrownTridentRenderer 26.3): T0 = rotY(-90)[·rotZ(90)],
|
||||
// pitchAxis = -X, so rotY(yaw)·rotAxis(-X, pitch)·T0 == vanilla rotY(yRot-90)·rotZ(xRot[+90]) with the
|
||||
// raw projectile rotation (yaw atan2(dx,dz), pitch positive up); origin = entity position, no offset.
|
||||
if (info != null && info.pitchAxis() != null && s.pitch() != 0) {
|
||||
double[] a = info.pitchAxis();
|
||||
place = place.mul(Affine.rotAxis(a[0], a[1], a[2], Math.toRadians(s.pitch())));
|
||||
|
||||
+33
-1
@@ -19,6 +19,7 @@ import java.util.function.DoubleSupplier;
|
||||
import org.bukkit.Keyed;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.AbstractArrow;
|
||||
import org.bukkit.entity.AbstractHorse;
|
||||
import org.bukkit.entity.Ageable;
|
||||
import org.bukkit.entity.ArmorStand;
|
||||
@@ -47,6 +48,7 @@ import org.bukkit.entity.Shulker;
|
||||
import org.bukkit.entity.Slime;
|
||||
import org.bukkit.entity.Snowman;
|
||||
import org.bukkit.entity.TraderLlama;
|
||||
import org.bukkit.entity.Trident;
|
||||
import org.bukkit.entity.TropicalFish;
|
||||
import org.bukkit.entity.Villager;
|
||||
import org.bukkit.entity.Wolf;
|
||||
@@ -304,7 +306,7 @@ public final class EntitySnapshotBuilder {
|
||||
bodyEquip,
|
||||
equipment,
|
||||
invisible,
|
||||
loc.getPitch(),
|
||||
loc.getPitch() + arrowShake(e),
|
||||
bodyDye,
|
||||
tint2,
|
||||
e.isInsideVehicle(),
|
||||
@@ -406,6 +408,36 @@ public final class EntitySnapshotBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
/** Per NMS entity class: its public {@code shakeTime} field (AbstractArrow), or null. */
|
||||
private static final ClassValue<java.lang.reflect.Field> SHAKE_FIELD = new ClassValue<>() {
|
||||
@Override
|
||||
protected java.lang.reflect.Field computeValue(Class<?> c) {
|
||||
try {
|
||||
return c.getField("shakeTime");
|
||||
} catch (NoSuchFieldException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Vanilla impact wobble of a freshly stuck arrow (ArrowModel#setupAnim, partialTick 0): the model root turns
|
||||
* by {@code -sin(3·shake)·shake} degrees about its z axis — the same axis as the pitch, so it is added to it.
|
||||
* {@code shakeTime} (7 → 0 ticks after hitting a block) has no API; read from the server entity. Tridents
|
||||
* ignore it (ThrownTridentRenderer has no shake).
|
||||
*/
|
||||
private static float arrowShake(Entity e) {
|
||||
if (!(e instanceof AbstractArrow) || e instanceof Trident) return 0;
|
||||
try {
|
||||
Object handle = e.getClass().getMethod("getHandle").invoke(e);
|
||||
java.lang.reflect.Field f = SHAKE_FIELD.get(handle.getClass());
|
||||
int shake = f == null ? 0 : f.getInt(handle);
|
||||
return shake <= 0 ? 0 : (float) (-Math.sin(shake * 3.0) * shake);
|
||||
} catch (ReflectiveOperationException | RuntimeException ex) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/** Per entity class: its {@code getVariant()} / {@code getWeatheringState()} getter, or null. */
|
||||
private static final ClassValue<java.lang.reflect.Method> VARIANT_GETTER = new ClassValue<>() {
|
||||
@Override
|
||||
|
||||
Reference in new issue
Block a user