Fix /rotate for NPCs, mobs and buildings past 180 degrees

/rotate took degrees but stored them on NPCs and mobs where the code expects
cos of half the yaw, so the client got NaN and the DB got degrees; /rotate
face passed radians the same way. Buildings used |cos| for w, which mirrored
any angle above 180 (270 became 90). Every kind now takes a yaw in radians
(degrees from the command, the facing angle for "face") through
Quaternion.fromYaw. NPC and mob yaws are wrapped to [0, 2 pi) so the stored
cos reloads to the same angle.

Value changes: /rotate N on NPCs/mobs now faces N degrees (was NaN);
buildings past 180 degrees now face the given angle; npc_rotation and
mob_rotation saved by /rotate hold cos of half the yaw instead of the raw
input.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
MagicBotandClaude Opus 5.5 committed 2026-10-27 06:43:28 -04:00
1 parent e288524ec0
commit bbf978872b
1 file changed
+22 -24
+22 -24
View File
@@ -14,6 +14,7 @@ import engine.devcmd.AbstractDevCmd;
import engine.gameManager.BuildingManager;
import engine.gameManager.ChatManager;
import engine.gameManager.DbManager;
import engine.math.FastMath;
import engine.math.Quaternion;
import engine.math.Vector3fImmutable;
import engine.objects.*;
@@ -64,11 +65,11 @@ public class RotateCmd extends AbstractDevCmd {
}
if (target instanceof Building)
rotateBuilding(pc, (Building) target, rot);
rotateBuilding(pc, (Building) target, (float) Math.toRadians(rot));
else if (target instanceof NPC)
rotateNPC(pc, (NPC) target, rot);
rotateNPC(pc, (NPC) target, (float) Math.toRadians(rot));
else if (target instanceof Mob)
rotateMob(pc, (Mob) target, rot);
rotateMob(pc, (Mob) target, (float) Math.toRadians(rot));
else
throwbackError(pc, "Target " + target.getObjectType()
+ " is not a valid object type");
@@ -145,7 +146,7 @@ public class RotateCmd extends AbstractDevCmd {
else
b = getTargetAsBuilding(pc);
if (b != null) {
rotateBuilding(pc, b, rot);
rotateBuilding(pc, b, (float) Math.toRadians(rot));
return;
}
@@ -156,7 +157,7 @@ public class RotateCmd extends AbstractDevCmd {
else
npc = getTargetAsNPC(pc);
if (npc != null) {
rotateNPC(pc, npc, rot);
rotateNPC(pc, npc, (float) Math.toRadians(rot));
return;
}
@@ -167,7 +168,7 @@ public class RotateCmd extends AbstractDevCmd {
else
mob = getTargetAsMob(pc);
if (mob != null) {
rotateMob(pc, mob, rot);
rotateMob(pc, mob, (float) Math.toRadians(rot));
return;
}
throwbackError(pc, "Nothing found to rotate.");
@@ -181,12 +182,11 @@ public class RotateCmd extends AbstractDevCmd {
@Override
protected String _getUsageString() {
return "' /rotate [objectID] rotation' || ' /rot [objectID] rotation'";
return "' /rotate [objectID] degrees' || ' /rot [objectID] degrees' || ' /rotate face'";
}
private void rotateBuilding(PlayerCharacter pc, Building building, float orig) {
building.setRotation(new Quaternion(0f, (float) Math.sin(Math.toRadians(orig) / 2), 0f,
(float) Math.abs(Math.cos(Math.toRadians(orig) / 2))));
private void rotateBuilding(PlayerCharacter pc, Building building, float yaw) {
building.setRotation(Quaternion.fromYaw(yaw));
building.getBounds().setBounds(building);
WorldGrid.updateObject(building, pc);
DbManager.BuildingQueries.SET_PROPERTY(building, "rotY", building.getRotation().y);
@@ -195,24 +195,27 @@ public class RotateCmd extends AbstractDevCmd {
"Building with ID " + building.getObjectUUID() + " rotated");
}
private void rotateNPC(PlayerCharacter pc, NPC npc, float rot) {
npc.setRotation(Quaternion.fromYaw((float) Math.acos(rot) * 2));
DbManager.NPCQueries.SET_PROPERTY(npc, "npc_rotation", rot);
private void rotateNPC(PlayerCharacter pc, NPC npc, float yaw) {
npc.setRotation(characterRotation(yaw));
DbManager.NPCQueries.SET_PROPERTY(npc, "npc_rotation", npc.getRotation().w);
WorldGrid.updateObject(npc, pc);
//no rotation for npc's in db currently
ChatManager.chatSayInfo(pc,
"NPC with ID " + npc.getObjectUUID() + " rotated");
}
private void rotateMob(PlayerCharacter pc, Mob mob, float rot) {
mob.setRotation(Quaternion.fromYaw((float) Math.acos(rot) * 2));
DbManager.MobQueries.SET_PROPERTY(mob, "mob_rotation", rot);
private void rotateMob(PlayerCharacter pc, Mob mob, float yaw) {
mob.setRotation(characterRotation(yaw));
DbManager.MobQueries.SET_PROPERTY(mob, "mob_rotation", mob.getRotation().w);
WorldGrid.updateObject(mob, pc);
//no rotation for mobs's in db currently
ChatManager.chatSayInfo(pc,
"Mob with ID " + mob.getObjectUUID() + " rotated");
}
// npc_rotation and mob_rotation store w = cos θ/2, which reloads to the same yaw only for θ in [0, 2π).
private static Quaternion characterRotation(float yaw) {
return Quaternion.fromYaw((float) (yaw - FastMath.TWO_PI * Math.floor(yaw / FastMath.TWO_PI)));
}
private void rotateFace(PlayerCharacter pc, AbstractGameObject target) {
AbstractWorldObject awo = (AbstractWorldObject) target;
if (awo == null)
@@ -224,13 +227,8 @@ public class RotateCmd extends AbstractDevCmd {
float rotangle = faceDirection.getRotation();
float rot = (float) Math.toDegrees(rotangle);
if (rot > 180)
rot *= -1;
if (target instanceof Building)
rotateBuilding(pc, (Building) target, rot);
rotateBuilding(pc, (Building) target, rotangle);
else if (target instanceof NPC)
rotateNPC(pc, (NPC) target, rotangle);
else if (target instanceof Mob)