Files
Server/src/engine/devcmd/cmds/RotateCmd.java
T
MagicBotandClaude Opus 5.5 bd3787f9d5 Refuse dev commands that would change static world objects
World Boot Task 0.6. Static objects are built from client data each boot,
so dev commands answer "Must use external tooling to modify gameworld" and
log the command, target and caller instead of changing them.

- /addbuilding, /mob and /npc always refuse (addsWorldContent hook in doCmd).
- rotate, pull, setbuildingaltitude, remove, rename, npcname, setowner,
  setguild, setrank and forcerename refuse a static target.
- purge skips static objects and reports how many it skipped.

Value changes: none for non-static objects; the purge message now also
reports the skipped count.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 18:57:40 -04:00

253 lines
9.0 KiB
Java

// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
// Magicbane Emulator Project © 2013 - 2026
// https://www.magicbane.com
package engine.devcmd.cmds;
import engine.InterestManagement.WorldGrid;
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.*;
public class RotateCmd extends AbstractDevCmd {
public RotateCmd() {
super("rotate");
this.addCmdString("rot");
}
@Override
protected void _doCmd(PlayerCharacter pc, String[] words,
AbstractGameObject target) {
if (target == null && (words.length != 2)) {
this.sendUsage(pc);
return;
}
if (words.length == 3) {
try {
} catch (Exception e) {
}
}
float rot;
if (target != null && words.length == 1) {
try {
if (words[0].equalsIgnoreCase("face")) {
this.rotateFace(pc, target);
return;
}
rot = Float.parseFloat(words[0]);
} catch (NumberFormatException e) {
throwbackError(pc, "Supplied rotation " + words[0]
+ " failed to parse to a Float");
return;
} catch (Exception e) {
throwbackError(pc,
"Invalid Rotate Command. Need Rotation specified.");
return;
}
if (target instanceof Building)
rotateBuilding(pc, (Building) target, (float) Math.toRadians(rot));
else if (target instanceof NPC)
rotateNPC(pc, (NPC) target, (float) Math.toRadians(rot));
else if (target instanceof Mob)
rotateMob(pc, (Mob) target, (float) Math.toRadians(rot));
else
throwbackError(pc, "Target " + target.getObjectType()
+ " is not a valid object type");
} else {
int id = 0;
if (words.length == 2) {
try {
id = Integer.parseInt(words[0]);
if (words[1].equalsIgnoreCase("face")) {
Building b;
if (id != 0)
b = BuildingManager.getBuilding(id);
else
b = getTargetAsBuilding(pc);
if (b != null) {
rotateFace(pc, b);
return;
}
// building failed, try npc
NPC npc;
if (id != 0)
npc = NPC.getNPC(id);
else
npc = getTargetAsNPC(pc);
if (npc != null) {
rotateFace(pc, npc);
return;
}
// NPC failed, try mob
Mob mob;
if (id != 0)
mob = Mob.getMob(id);
else
mob = getTargetAsMob(pc);
if (mob != null) {
rotateFace(pc, mob);
return;
}
throwbackError(pc, "Nothing found to rotate.");
return;
}
rot = Float.parseFloat(words[1]);
} catch (NumberFormatException e) {
throwbackError(pc, "Supplied arguments " + words[0] + ' '
+ words[1] + " failed to parse");
return;
} catch (Exception e) {
throwbackError(pc,
"Invalid Rotate Command. Need Rotation specified.");
return; // NaN
}
} else {
try {
rot = Float.parseFloat(words[0]);
} catch (NumberFormatException e) {
throwbackError(pc, "Supplied rotation " + words[0]
+ " failed to parse to a Float");
return;
} catch (Exception e) {
throwbackError(pc,
"Invalid Rotate Command. Need Rotation specified.");
return; // NaN
}
}
Building b;
if (id != 0)
b = BuildingManager.getBuilding(id);
else
b = getTargetAsBuilding(pc);
if (b != null) {
rotateBuilding(pc, b, (float) Math.toRadians(rot));
return;
}
// building failed, try npc
NPC npc;
if (id != 0)
npc = NPC.getNPC(id);
else
npc = getTargetAsNPC(pc);
if (npc != null) {
rotateNPC(pc, npc, (float) Math.toRadians(rot));
return;
}
// NPC failed, try mob
Mob mob;
if (id != 0)
mob = Mob.getMob(id);
else
mob = getTargetAsMob(pc);
if (mob != null) {
rotateMob(pc, mob, (float) Math.toRadians(rot));
return;
}
throwbackError(pc, "Nothing found to rotate.");
}
}
@Override
protected String _getHelpString() {
return "Rotates targeted or specified object";
}
@Override
protected String _getUsageString() {
return "' /rotate [objectID] degrees' || ' /rot [objectID] degrees' || ' /rotate face'";
}
private void rotateBuilding(PlayerCharacter pc, Building building, float yaw) {
if (this.refuseStaticTarget(pc, building))
return;
building.setRotation(Quaternion.fromYaw(yaw));
building.getBounds().setBounds(building);
WorldGrid.updateObject(building, pc);
DbManager.BuildingQueries.SET_PROPERTY(building, "rotY", building.getRotation().y);
DbManager.BuildingQueries.SET_PROPERTY(building, "w", building.getRotation().w);
ChatManager.chatSayInfo(pc,
"Building with ID " + building.getObjectUUID() + " rotated");
}
private void rotateNPC(PlayerCharacter pc, NPC npc, float yaw) {
if (this.refuseStaticTarget(pc, npc))
return;
npc.setRotation(characterRotation(yaw));
DbManager.NPCQueries.SET_PROPERTY(npc, "npc_rotation", npc.getRotation().w);
WorldGrid.updateObject(npc, pc);
ChatManager.chatSayInfo(pc,
"NPC with ID " + npc.getObjectUUID() + " rotated");
}
private void rotateMob(PlayerCharacter pc, Mob mob, float yaw) {
if (this.refuseStaticTarget(pc, mob))
return;
mob.setRotation(characterRotation(yaw));
DbManager.MobQueries.SET_PROPERTY(mob, "mob_rotation", mob.getRotation().w);
WorldGrid.updateObject(mob, pc);
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)
return;
Vector3fImmutable buildingLoc = awo.getLoc();
Vector3fImmutable playerLoc = pc.getLoc();
Vector3fImmutable faceDirection = playerLoc.subtract2D(buildingLoc);
float rotangle = faceDirection.getRotation();
if (target instanceof Building)
rotateBuilding(pc, (Building) target, rotangle);
else if (target instanceof NPC)
rotateNPC(pc, (NPC) target, rotangle);
else if (target instanceof Mob)
rotateMob(pc, (Mob) target, rotangle);
else
throwbackError(pc, "Target " + target.getObjectType()
+ " is not a valid object type");
}
}