From 701cb94ba6633decb80de5cc13397ee5c9f23149 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Tue, 10 Mar 2026 13:11:50 -0700 Subject: [PATCH] physics: apply server walk and swim speed overrides to CameraController serverWalkSpeed_ and serverSwimSpeed_ were stored in GameHandler but never exposed or synced to the camera controller. The controller used hardcoded WOW_WALK_SPEED and speed*SWIM_SPEED_FACTOR regardless of server-sent speed changes. Add getServerWalkSpeed()/getServerSwimSpeed() accessors, walkSpeedOverride_ and swimSpeedOverride_ fields in CameraController, and sync all three server speeds each frame. Both swim speed sites (main and camera-collision path) now use the override when set. This makes Slow debuffs (walk speed), Swim Form, and Engineering fins actually affect movement speed. --- include/game/game_handler.hpp | 2 ++ include/rendering/camera_controller.hpp | 6 +++++- src/core/application.cpp | 2 ++ src/rendering/camera_controller.cpp | 9 ++++++--- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/include/game/game_handler.hpp b/include/game/game_handler.hpp index 60afeb23..481a2381 100644 --- a/include/game/game_handler.hpp +++ b/include/game/game_handler.hpp @@ -1152,6 +1152,8 @@ public: bool isMounted() const { return currentMountDisplayId_ != 0; } bool isHostileAttacker(uint64_t guid) const { return hostileAttackers_.count(guid) > 0; } float getServerRunSpeed() const { return serverRunSpeed_; } + float getServerWalkSpeed() const { return serverWalkSpeed_; } + float getServerSwimSpeed() const { return serverSwimSpeed_; } bool isPlayerRooted() const { return (movementInfo.flags & static_cast(MovementFlags::ROOT)) != 0; } diff --git a/include/rendering/camera_controller.hpp b/include/rendering/camera_controller.hpp index d42ec5c0..fc770d6f 100644 --- a/include/rendering/camera_controller.hpp +++ b/include/rendering/camera_controller.hpp @@ -92,6 +92,8 @@ public: void setMovementCallback(MovementCallback cb) { movementCallback = std::move(cb); } void setUseWoWSpeed(bool use) { useWoWSpeed = use; } void setRunSpeedOverride(float speed) { runSpeedOverride_ = speed; } + void setWalkSpeedOverride(float speed) { walkSpeedOverride_ = speed; } + void setSwimSpeedOverride(float speed) { swimSpeedOverride_ = speed; } void setMovementRooted(bool rooted) { movementRooted_ = rooted; } bool isMovementRooted() const { return movementRooted_; } void setGravityDisabled(bool disabled) { gravityDisabled_ = disabled; } @@ -269,8 +271,10 @@ private: return std::sqrt(2.0f * std::abs(MOUNT_GRAVITY) * MOUNT_JUMP_HEIGHT); } - // Server-driven run speed override (0 = use default WOW_RUN_SPEED) + // Server-driven speed overrides (0 = use hardcoded default) float runSpeedOverride_ = 0.0f; + float walkSpeedOverride_ = 0.0f; + float swimSpeedOverride_ = 0.0f; // Server-driven root state: when true, block all horizontal movement input. bool movementRooted_ = false; // Server-driven gravity disable (levitate/hover): skip gravity accumulation. diff --git a/src/core/application.cpp b/src/core/application.cpp index 06c43ff5..5eb188b3 100644 --- a/src/core/application.cpp +++ b/src/core/application.cpp @@ -1009,6 +1009,8 @@ void Application::update(float deltaTime) { runInGameStage("post-update sync", [&] { if (renderer && gameHandler && renderer->getCameraController()) { renderer->getCameraController()->setRunSpeedOverride(gameHandler->getServerRunSpeed()); + renderer->getCameraController()->setWalkSpeedOverride(gameHandler->getServerWalkSpeed()); + renderer->getCameraController()->setSwimSpeedOverride(gameHandler->getServerSwimSpeed()); renderer->getCameraController()->setMovementRooted(gameHandler->isPlayerRooted()); renderer->getCameraController()->setGravityDisabled(gameHandler->isGravityDisabled()); } diff --git a/src/rendering/camera_controller.cpp b/src/rendering/camera_controller.cpp index b8f4ce30..94eadaac 100644 --- a/src/rendering/camera_controller.cpp +++ b/src/rendering/camera_controller.cpp @@ -320,7 +320,8 @@ void CameraController::update(float deltaTime) { if (nowBackward && !nowForward) { speed = WOW_BACK_SPEED; } else if (ctrlDown) { - speed = WOW_WALK_SPEED; + speed = (walkSpeedOverride_ > 0.0f && walkSpeedOverride_ < 100.0f && !std::isnan(walkSpeedOverride_)) + ? walkSpeedOverride_ : WOW_WALK_SPEED; } else if (runSpeedOverride_ > 0.0f && runSpeedOverride_ < 100.0f && !std::isnan(runSpeedOverride_)) { speed = runSpeedOverride_; } else { @@ -507,7 +508,8 @@ void CameraController::update(float deltaTime) { swimming = true; // Swim movement follows look pitch (forward/back), while strafe stays // lateral for stable control. - float swimSpeed = speed * SWIM_SPEED_FACTOR; + float swimSpeed = (swimSpeedOverride_ > 0.0f && swimSpeedOverride_ < 100.0f && !std::isnan(swimSpeedOverride_)) + ? swimSpeedOverride_ : speed * SWIM_SPEED_FACTOR; float waterSurfaceZ = waterH ? (*waterH - WATER_SURFACE_OFFSET) : targetPos.z; // For auto-run/auto-swim: use character facing (immune to camera pan) @@ -1518,7 +1520,8 @@ void CameraController::update(float deltaTime) { if (inWater) { swimming = true; - float swimSpeed = speed * SWIM_SPEED_FACTOR; + float swimSpeed = (swimSpeedOverride_ > 0.0f && swimSpeedOverride_ < 100.0f && !std::isnan(swimSpeedOverride_)) + ? swimSpeedOverride_ : speed * SWIM_SPEED_FACTOR; float waterSurfaceCamZ = waterH ? (*waterH - WATER_SURFACE_OFFSET + eyeHeight) : newPos.z; bool diveIntent = nowForward && (forward3D.z < -0.28f);