|
| 1 | +/* |
| 2 | + * GM/dev commands for the Movement subsystem: .movement status/config/set. |
| 3 | + * Get/inspect movement state, and live-tune the movement config (smoothing + |
| 4 | + * global player speed rate), mirroring the .timesync command pattern. |
| 5 | + */ |
| 6 | + |
| 7 | +#include "Chat.h" |
| 8 | +#include "Player.h" |
| 9 | +#include "World.h" |
| 10 | +#include "ObjectAccessor.h" |
| 11 | +#include "Log.h" |
| 12 | + |
| 13 | +#include <cstring> |
| 14 | +#include <cctype> |
| 15 | +#include <cstdlib> |
| 16 | + |
| 17 | +bool ChatHandler::HandleMovementStatusCommand(char* /*args*/) |
| 18 | +{ |
| 19 | + Player* t = getSelectedPlayer(); |
| 20 | + if (!t) |
| 21 | + t = m_session ? m_session->GetPlayer() : NULL; |
| 22 | + if (!t) |
| 23 | + { |
| 24 | + SendSysMessage(".movement status FAILED: no player. Select/target a player or run in-game."); |
| 25 | + SetSentErrorMessage(true); |
| 26 | + return false; |
| 27 | + } |
| 28 | + |
| 29 | + PSendSysMessage("Movement %s: pos(%.1f, %.1f, %.1f) run=%.1f walk=%.1f swim=%.1f flags=0x%X", |
| 30 | + t->GetName(), t->GetPositionX(), t->GetPositionY(), t->GetPositionZ(), |
| 31 | + t->GetSpeed(MOVE_RUN), t->GetSpeed(MOVE_WALK), t->GetSpeed(MOVE_SWIM), |
| 32 | + t->m_movementInfo.GetMovementFlags()); |
| 33 | + return true; |
| 34 | +} |
| 35 | + |
| 36 | +bool ChatHandler::HandleMovementConfigCommand(char* /*args*/) |
| 37 | +{ |
| 38 | + PSendSysMessage("Movement config: smoothing=%u heartbeatMs=%u maxExtrapolateMs=%u", |
| 39 | + (uint32)sWorld.getConfig(CONFIG_BOOL_MOVEMENT_SMOOTHING), |
| 40 | + sWorld.getConfig(CONFIG_UINT32_MOVEMENT_HEARTBEAT_MS), |
| 41 | + sWorld.getConfig(CONFIG_UINT32_MOVEMENT_MAX_EXTRAPOLATE_MS)); |
| 42 | + PSendSysMessage(" speedRate=%u%% (run=%u%% swim=%u%% walk=%u%%)", |
| 43 | + sWorld.getConfig(CONFIG_UINT32_MOVEMENT_SPEED_RATE), |
| 44 | + sWorld.getConfig(CONFIG_UINT32_MOVEMENT_RUN_RATE), |
| 45 | + sWorld.getConfig(CONFIG_UINT32_MOVEMENT_SWIM_RATE), |
| 46 | + sWorld.getConfig(CONFIG_UINT32_MOVEMENT_WALK_RATE)); |
| 47 | + SendSysMessage("Set at runtime with .movement set <field> <value> (reverts on restart/reload)."); |
| 48 | + return true; |
| 49 | +} |
| 50 | + |
| 51 | +bool ChatHandler::HandleMovementSetCommand(char* args) |
| 52 | +{ |
| 53 | + char* f = strtok(args, " "); |
| 54 | + char* v = strtok(NULL, " "); |
| 55 | + if (!f || !v) |
| 56 | + { |
| 57 | + SendSysMessage(".movement set FAILED. Usage: .movement set <field> <value>. Fields:"); |
| 58 | + SendSysMessage(" smoothing (0/1), heartbeatms (100-2000), maxextrapolatems (100-3000),"); |
| 59 | + SendSysMessage(" speedrate / run / swim / walk (10-1000, percent of normal; apply live)"); |
| 60 | + SetSentErrorMessage(true); |
| 61 | + return false; |
| 62 | + } |
| 63 | + std::string field = f; |
| 64 | + for (size_t i = 0; i < field.size(); ++i) field[i] = (char)tolower(field[i]); |
| 65 | + int32 val = atoi(v); |
| 66 | + bool refreshSpeed = false; |
| 67 | + |
| 68 | + if (field == "smoothing") |
| 69 | + { |
| 70 | + sWorld.setConfig(CONFIG_BOOL_MOVEMENT_SMOOTHING, val != 0); |
| 71 | + } |
| 72 | + else if (field == "heartbeatms") |
| 73 | + { |
| 74 | + if (val < 100) val = 100; if (val > 2000) val = 2000; |
| 75 | + sWorld.setConfig(CONFIG_UINT32_MOVEMENT_HEARTBEAT_MS, uint32(val)); |
| 76 | + } |
| 77 | + else if (field == "maxextrapolatems") |
| 78 | + { |
| 79 | + if (val < 100) val = 100; if (val > 3000) val = 3000; |
| 80 | + sWorld.setConfig(CONFIG_UINT32_MOVEMENT_MAX_EXTRAPOLATE_MS, uint32(val)); |
| 81 | + } |
| 82 | + else if (field == "speedrate" || field == "run" || field == "swim" || field == "walk") |
| 83 | + { |
| 84 | + if (val < 10) val = 10; if (val > 1000) val = 1000; |
| 85 | + if (field == "speedrate") sWorld.setConfig(CONFIG_UINT32_MOVEMENT_SPEED_RATE, uint32(val)); |
| 86 | + else if (field == "run") sWorld.setConfig(CONFIG_UINT32_MOVEMENT_RUN_RATE, uint32(val)); |
| 87 | + else if (field == "swim") sWorld.setConfig(CONFIG_UINT32_MOVEMENT_SWIM_RATE, uint32(val)); |
| 88 | + else sWorld.setConfig(CONFIG_UINT32_MOVEMENT_WALK_RATE, uint32(val)); |
| 89 | + refreshSpeed = true; |
| 90 | + } |
| 91 | + else |
| 92 | + { |
| 93 | + PSendSysMessage(".movement set FAILED: unknown field '%s'.", field.c_str()); |
| 94 | + SetSentErrorMessage(true); |
| 95 | + return false; |
| 96 | + } |
| 97 | + |
| 98 | + if (refreshSpeed) |
| 99 | + { |
| 100 | + // Apply live: refresh every online player's speeds (forced => clients told). |
| 101 | + sObjectAccessor.DoForAllPlayers([](Player* p) |
| 102 | + { |
| 103 | + if (!p) return; |
| 104 | + for (int mt = 0; mt < MAX_MOVE_TYPE; ++mt) |
| 105 | + p->UpdateSpeed(UnitMoveType(mt), true); |
| 106 | + }); |
| 107 | + } |
| 108 | + PSendSysMessage("Movement: set %s = %d (runtime; reverts on restart/reload from file).", field.c_str(), val); |
| 109 | + return true; |
| 110 | +} |
0 commit comments