diff --git a/include/rendering/camera_controller.hpp b/include/rendering/camera_controller.hpp index 3337a755..7401ffdd 100644 --- a/include/rendering/camera_controller.hpp +++ b/include/rendering/camera_controller.hpp @@ -90,6 +90,11 @@ public: // Movement callback for sending opcodes to server using MovementCallback = std::function; void setMovementCallback(MovementCallback cb) { movementCallback = std::move(cb); } + + // Callback invoked when the player stands up via local input (space/X/movement key + // while server-sitting), so the caller can send CMSG_STAND_STATE_CHANGE(0). + using StandUpCallback = std::function; + void setStandUpCallback(StandUpCallback cb) { standUpCallback_ = std::move(cb); } void setUseWoWSpeed(bool use) { useWoWSpeed = use; } void setRunSpeedOverride(float speed) { runSpeedOverride_ = speed; } void setWalkSpeedOverride(float speed) { walkSpeedOverride_ = speed; } @@ -265,6 +270,7 @@ private: // Movement callback MovementCallback movementCallback; + StandUpCallback standUpCallback_; // Movement speeds bool useWoWSpeed = false; diff --git a/src/core/application.cpp b/src/core/application.cpp index 35ebca5f..91126c66 100644 --- a/src/core/application.cpp +++ b/src/core/application.cpp @@ -628,6 +628,11 @@ void Application::setState(AppState newState) { gameHandler->sendMovement(static_cast(opcode)); } }); + cc->setStandUpCallback([this]() { + if (gameHandler) { + gameHandler->setStandState(0); // CMSG_STAND_STATE_CHANGE(STAND) + } + }); cc->setUseWoWSpeed(true); } if (gameHandler) { diff --git a/src/rendering/camera_controller.cpp b/src/rendering/camera_controller.cpp index cfa6120a..77908f3a 100644 --- a/src/rendering/camera_controller.cpp +++ b/src/rendering/camera_controller.cpp @@ -369,6 +369,7 @@ void CameraController::update(float deltaTime) { // Toggle sit/crouch with X key (edge-triggered) — only when UI doesn't want keyboard // Blocked while mounted + bool prevSitting = sitting; bool xDown = !uiWantsKeyboard && input.isKeyPressed(SDL_SCANCODE_X); if (xDown && !xKeyWasDown && !mounted_) { sitting = !sitting; @@ -376,6 +377,21 @@ void CameraController::update(float deltaTime) { if (mounted_) sitting = false; xKeyWasDown = xDown; + // Stand up on any movement key or jump while sitting (WoW behaviour) + if (!uiWantsKeyboard && sitting && !movementSuppressed) { + bool anyMoveKey = + input.isKeyPressed(SDL_SCANCODE_W) || input.isKeyPressed(SDL_SCANCODE_S) || + input.isKeyPressed(SDL_SCANCODE_A) || input.isKeyPressed(SDL_SCANCODE_D) || + input.isKeyPressed(SDL_SCANCODE_Q) || input.isKeyPressed(SDL_SCANCODE_E) || + input.isKeyPressed(SDL_SCANCODE_SPACE); + if (anyMoveKey) sitting = false; + } + + // Notify server when the player stands up via local input + if (prevSitting && !sitting && standUpCallback_) { + standUpCallback_(); + } + // Update eye height based on crouch state (smooth transition) float targetEyeHeight = sitting ? CROUCH_EYE_HEIGHT : STAND_EYE_HEIGHT; float heightLerpSpeed = 10.0f * deltaTime; @@ -389,11 +405,6 @@ void CameraController::update(float deltaTime) { if (nowStrafeLeft) movement += right; if (nowStrafeRight) movement -= right; - // Stand up if jumping while crouched - if (!uiWantsKeyboard && sitting && input.isKeyPressed(SDL_SCANCODE_SPACE)) { - sitting = false; - } - // Third-person orbit camera mode if (thirdPerson && followTarget) { // Move the follow target (character position) instead of the camera diff --git a/src/ui/game_screen.cpp b/src/ui/game_screen.cpp index 3b3c7216..e41909d6 100644 --- a/src/ui/game_screen.cpp +++ b/src/ui/game_screen.cpp @@ -5108,9 +5108,13 @@ void GameScreen::renderNameplates(game::GameHandler& gameHandler) { // Player nameplates are always shown; NPC nameplates respect the V-key toggle if (!isPlayer && !showNameplates_) continue; - // Convert canonical WoW position → render space, raise to head height - glm::vec3 renderPos = core::coords::canonicalToRender( - glm::vec3(unit->getX(), unit->getY(), unit->getZ())); + // Prefer the renderer's actual instance position so the nameplate tracks the + // rendered model exactly (avoids drift from the parallel entity interpolator). + glm::vec3 renderPos; + if (!core::Application::getInstance().getRenderPositionForGuid(guid, renderPos)) { + renderPos = core::coords::canonicalToRender( + glm::vec3(unit->getX(), unit->getY(), unit->getZ())); + } renderPos.z += 2.3f; // Cull distance: target or other players up to 40 units; NPC others up to 20 units