From 234c99b2afcc48cfe5c0f1d0bc2560753e2adcba Mon Sep 17 00:00:00 2001 From: fullwall Date: Mon, 25 Apr 2022 22:29:26 +0800 Subject: [PATCH] Refactor rotation more --- .../npc/CitizensTraitFactory.java | 4 +- .../net/citizensnpcs/trait/RotationTrait.java | 111 -------- .../trait/SmoothRotationTrait.java | 239 ++++++++++++++++++ 3 files changed, 241 insertions(+), 113 deletions(-) delete mode 100644 main/src/main/java/net/citizensnpcs/trait/RotationTrait.java create mode 100644 main/src/main/java/net/citizensnpcs/trait/SmoothRotationTrait.java diff --git a/main/src/main/java/net/citizensnpcs/npc/CitizensTraitFactory.java b/main/src/main/java/net/citizensnpcs/npc/CitizensTraitFactory.java index 7d61e1096..9b1e62543 100644 --- a/main/src/main/java/net/citizensnpcs/npc/CitizensTraitFactory.java +++ b/main/src/main/java/net/citizensnpcs/npc/CitizensTraitFactory.java @@ -42,7 +42,7 @@ import net.citizensnpcs.trait.OcelotModifiers; import net.citizensnpcs.trait.Poses; import net.citizensnpcs.trait.Powered; import net.citizensnpcs.trait.RabbitType; -import net.citizensnpcs.trait.RotationTrait; +import net.citizensnpcs.trait.SmoothRotationTrait; import net.citizensnpcs.trait.Saddle; import net.citizensnpcs.trait.ScoreboardTrait; import net.citizensnpcs.trait.ScriptTrait; @@ -89,7 +89,7 @@ public class CitizensTraitFactory implements TraitFactory { registerTrait(TraitInfo.create(Poses.class)); registerTrait(TraitInfo.create(Powered.class)); registerTrait(TraitInfo.create(RabbitType.class)); - registerTrait(TraitInfo.create(RotationTrait.class)); + registerTrait(TraitInfo.create(SmoothRotationTrait.class)); registerTrait(TraitInfo.create(Saddle.class)); registerTrait(TraitInfo.create(ScoreboardTrait.class)); registerTrait(TraitInfo.create(ScriptTrait.class)); diff --git a/main/src/main/java/net/citizensnpcs/trait/RotationTrait.java b/main/src/main/java/net/citizensnpcs/trait/RotationTrait.java deleted file mode 100644 index 31f54ef0b..000000000 --- a/main/src/main/java/net/citizensnpcs/trait/RotationTrait.java +++ /dev/null @@ -1,111 +0,0 @@ -package net.citizensnpcs.trait; - -import org.bukkit.Location; -import org.bukkit.entity.Entity; - -import net.citizensnpcs.api.trait.Trait; -import net.citizensnpcs.api.trait.TraitName; -import net.citizensnpcs.util.NMS; -import net.citizensnpcs.util.Util; - -@TraitName("rotationtrait") -public class RotationTrait extends Trait { - protected float maxPitchRotationPerTick = 10; - protected float maxYawRotationPerTick = 40; - protected boolean rotating; - protected double tx; - protected double ty; - protected double tz; - - public RotationTrait() { - super("rotationtrait"); - } - - private double getEyeY() { - return NMS.getHeight(npc.getEntity()); - } - - protected float getTargetPitchDifference() { - double dx = tx - getX(); - double dy = ty - (getY() + getEyeY()); - double dz = tz - getZ(); - double diag = Math.sqrt((float) (dx * dx + dz * dz)); - return (float) -Math.toDegrees(Math.atan2(dy, diag)); - } - - protected float getTargetYawDifference() { - return (float) Math.toDegrees(Math.atan2(tz - getZ(), tx - getX())) - 90.0F; - } - - private double getX() { - return npc.getStoredLocation().getX(); - } - - private double getY() { - return npc.getStoredLocation().getY(); - } - - private double getZ() { - return npc.getStoredLocation().getZ(); - } - - public void rotateToFace(Entity target) { - Location loc = target.getLocation(); - loc.setY(loc.getY() + NMS.getHeight(target)); - rotateToFace(loc); - } - - public void rotateToFace(Location target) { - this.tx = target.getX(); - this.ty = target.getY(); - this.tz = target.getZ(); - this.rotating = true; - } - - protected float rotateTowards(float target, float current, float maxRotPerTick) { - float diff = Util.clamp(current - target); - return target + clamp(diff, -maxRotPerTick, maxRotPerTick); - } - - @Override - public void run() { - if (!npc.isSpawned() || npc.getNavigator().isNavigating()) { - // npc.yHeadRot = Mth.rotateIfNecessary(npc.yHeadRot, npc.yBodyRot, 75); - return; - } - if (true) { - // npc.setXRot(0.0F); - } - if (this.rotating) { - this.rotating = false; - NMS.setHeadYaw(npc.getEntity(), Util.clamp(rotateTowards(NMS.getHeadYaw(npc.getEntity()), - getTargetYawDifference(), this.maxYawRotationPerTick))); - float d = Util.clamp(NMS.getHeadYaw(npc.getEntity()) - 40); - if (d > NMS.getYaw(npc.getEntity())) { - NMS.setBodyYaw(npc.getEntity(), d); - } - if (d != NMS.getYaw(npc.getEntity())) { - d = NMS.getHeadYaw(npc.getEntity()) + 40; - while (d >= 180F) { - d -= 360F; - } - while (d < -180F) { - d += 360F; - } - if (d < NMS.getYaw(npc.getEntity())) { - NMS.setBodyYaw(npc.getEntity(), d); - } - } - NMS.setPitch(npc.getEntity(), rotateTowards(npc.getStoredLocation().getPitch(), getTargetPitchDifference(), - this.maxPitchRotationPerTick)); - } - } - - public static float clamp(float var0, float var1, float var2) { - if (var0 < var1) { - return var1; - } else { - return var0 > var2 ? var2 : var0; - } - } -} diff --git a/main/src/main/java/net/citizensnpcs/trait/SmoothRotationTrait.java b/main/src/main/java/net/citizensnpcs/trait/SmoothRotationTrait.java new file mode 100644 index 000000000..5eb19c33d --- /dev/null +++ b/main/src/main/java/net/citizensnpcs/trait/SmoothRotationTrait.java @@ -0,0 +1,239 @@ +package net.citizensnpcs.trait; + +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.bukkit.util.Vector; + +import net.citizensnpcs.api.persistence.Persist; +import net.citizensnpcs.api.persistence.Persistable; +import net.citizensnpcs.api.trait.Trait; +import net.citizensnpcs.api.trait.TraitName; +import net.citizensnpcs.api.util.DataKey; +import net.citizensnpcs.util.NMS; +import net.citizensnpcs.util.Util; + +@TraitName("smoothrotationtrait") +public class SmoothRotationTrait extends Trait { + @Persist + private Float defaultPitch; + @Persist(reify = true) + private final RotationParams globalParameters = new RotationParams(); + private final SmoothRotationSession globalSession = new SmoothRotationSession(globalParameters); + + public SmoothRotationTrait() { + super("smoothrotationtrait"); + } + + private double getEyeY() { + return NMS.getHeight(npc.getEntity()); + } + + public RotationParams getGlobalParameters() { + return globalParameters; + } + + private double getX() { + return npc.getStoredLocation().getX(); + } + + private double getY() { + return npc.getStoredLocation().getY(); + } + + private double getZ() { + return npc.getStoredLocation().getZ(); + } + + public void rotateToFace(Entity target) { + Location loc = target.getLocation(); + loc.setY(loc.getY() + NMS.getHeight(target)); + rotateToFace(loc); + } + + public void rotateToFace(Location target) { + this.globalSession.setTarget(target); + } + + public void rotateToHave(float yaw, float pitch) { + Vector vector = new Vector(Math.cos(yaw) * Math.cos(pitch), Math.sin(pitch), Math.sin(yaw) * Math.cos(pitch)) + .normalize(); + rotateToFace(npc.getEntity().getLocation().clone().add(vector)); + } + + @Override + public void run() { + if (!npc.isSpawned() || npc.getNavigator().isNavigating()) { + // npc.yHeadRot = Mth.rotateIfNecessary(npc.yHeadRot, npc.yBodyRot, 75); + return; + } + if (!globalSession.hasTarget()) { + return; + } + EntityRotation rot = new EntityRotation(npc.getEntity()); + globalSession.run(rot); + if (!globalSession.hasTarget()) { + rot.bodyYaw = rot.headYaw; + } + rot.apply(npc.getEntity()); + } + + public void setDefaultPitch(float pitch) { + defaultPitch = pitch; + } + + public static class EntityRotation { + public float bodyYaw, headYaw, pitch; + + public EntityRotation(Entity entity) { + this.bodyYaw = NMS.getYaw(entity); + this.headYaw = NMS.getHeadYaw(entity); + this.pitch = entity.getLocation().getPitch(); + } + + public void apply(Entity entity) { + NMS.setBodyYaw(entity, bodyYaw); + NMS.setHeadYaw(entity, headYaw); + NMS.setPitch(entity, pitch); + } + } + + public static class RotationParams implements Persistable { + private boolean headOnly = false; + private boolean immediate = false; + private float maxPitchPerTick = 10; + private float maxYawPerTick = 40; + private final float[] pitchRange = { 0, 0 }; + private final float[] yawRange = { 0, 0 }; + + public RotationParams headOnly(boolean headOnly) { + this.headOnly = headOnly; + return this; + } + + public RotationParams immediate(boolean immediate) { + this.immediate = immediate; + return this; + } + + @Override + public void load(DataKey key) { + if (key.keyExists("headOnly")) { + headOnly = key.getBoolean("headOnly"); + } + if (key.keyExists("immediate")) { + immediate = key.getBoolean("immediate"); + } + if (key.keyExists("maxPitchPerTick")) { + maxPitchPerTick = (float) key.getDouble("maxPitchPerTick"); + } + if (key.keyExists("maxYawPerTick")) { + maxYawPerTick = (float) key.getDouble("maxYawPerTick"); + } + } + + public RotationParams maxPitchPerTick(float val) { + this.maxPitchPerTick = val; + return this; + } + + public RotationParams maxYawPerTick(float val) { + this.maxYawPerTick = val; + return this; + } + + public float rotateHeadYawTowards(int t, float yaw, float targetYaw) { + return rotateTowards(yaw, targetYaw, maxYawPerTick); + } + + public float rotatePitchTowards(int t, float pitch, float targetPitch) { + return rotateTowards(pitch, targetPitch, maxPitchPerTick); + }/* + * public Vector3 SuperSmoothVector3Lerp( Vector3 pastPosition, Vector3 pastTargetPosition, Vector3 targetPosition, float time, float speed ){ + Vector3 f = pastPosition - pastTargetPosition + (targetPosition - pastTargetPosition) / (speed * time); + return targetPosition - (targetPosition - pastTargetPosition) / (speed*time) + f * Mathf.Exp(-speed*time); + } + */ + + private float rotateTowards(float target, float current, float maxRotPerTick) { + float diff = Util.clamp(current - target); + return target + clamp(diff, -maxRotPerTick, maxRotPerTick); + } + + @Override + public void save(DataKey key) { + if (headOnly) { + key.setBoolean("headOnly", headOnly); + } + if (immediate) { + key.setBoolean("immediate", immediate); + } + if (maxPitchPerTick != 10) { + key.setDouble("maxPitchPerTick", maxPitchPerTick); + } + if (maxYawPerTick != 40) { + key.setDouble("maxYawPerTick", maxYawPerTick); + } + } + } + + public class SmoothRotationSession { + private final RotationParams params; + private int t; + private double tx, ty, tz; + + public SmoothRotationSession(RotationParams params) { + this.params = params; + } + + private float getTargetPitch() { + double dx = tx - getX(); + double dy = ty - (getY() + getEyeY()); + double dz = tz - getZ(); + double diag = Math.sqrt((float) (dx * dx + dz * dz)); + return (float) -Math.toDegrees(Math.atan2(dy, diag)); + } + + private float getTargetYaw() { + return (float) Math.toDegrees(Math.atan2(tz - getZ(), tx - getX())) - 90.0F; + } + + public boolean hasTarget() { + return t >= 0; + } + + public void run(EntityRotation rot) { + if (!hasTarget()) + return; + rot.headYaw = params.immediate ? getTargetYaw() + : Util.clamp(params.rotateHeadYawTowards(t, rot.headYaw, getTargetYaw())); + if (!params.headOnly) { + float d = Util.clamp(rot.headYaw - 35); + if (d > rot.bodyYaw) { + rot.bodyYaw = d; + } + if (d != rot.bodyYaw) { + d = Util.clamp(rot.headYaw + 35); + if (d < rot.bodyYaw) { + rot.bodyYaw = d; + } + } + } + rot.pitch = params.immediate ? getTargetPitch() : params.rotatePitchTowards(t, rot.pitch, getTargetPitch()); + t++; + if (Math.abs(rot.pitch - getTargetPitch()) + Math.abs(rot.headYaw - getTargetYaw()) < 0.1) { + t = -1; + } + } + + public void setTarget(Location target) { + tx = target.getX(); + ty = target.getY(); + tz = target.getZ(); + t = 0; + } + } + + private static float clamp(float orig, float min, float max) { + return Math.max(min, Math.min(max, orig)); + } +}