diff --git a/include/game/game_handler.hpp b/include/game/game_handler.hpp index fb1b0393..c26f152b 100644 --- a/include/game/game_handler.hpp +++ b/include/game/game_handler.hpp @@ -1166,6 +1166,11 @@ public: bool isWaterWalking() const { return (movementInfo.flags & static_cast(MovementFlags::WATER_WALK)) != 0; } + bool isPlayerFlying() const { + const uint32_t flyMask = static_cast(MovementFlags::CAN_FLY) | + static_cast(MovementFlags::FLYING); + return (movementInfo.flags & flyMask) == flyMask; + } void dismount(); // Taxi / Flight Paths diff --git a/include/rendering/camera_controller.hpp b/include/rendering/camera_controller.hpp index 58fde4a1..754de661 100644 --- a/include/rendering/camera_controller.hpp +++ b/include/rendering/camera_controller.hpp @@ -99,6 +99,7 @@ public: void setGravityDisabled(bool disabled) { gravityDisabled_ = disabled; } void setFeatherFallActive(bool active) { featherFallActive_ = active; } void setWaterWalkActive(bool active) { waterWalkActive_ = active; } + void setFlyingActive(bool active) { flyingActive_ = active; } void setMounted(bool m) { mounted_ = m; } void setMountHeightOffset(float offset) { mountHeightOffset_ = offset; } void setExternalFollow(bool enabled) { externalFollow_ = enabled; } @@ -285,6 +286,8 @@ private: bool featherFallActive_ = false; // Server-driven water walk: treat water surface as ground (don't swim). bool waterWalkActive_ = false; + // Player-controlled flight (CAN_FLY + FLYING): 3D movement, no gravity. + bool flyingActive_ = false; bool mounted_ = false; float mountHeightOffset_ = 0.0f; bool externalMoving_ = false; diff --git a/src/core/application.cpp b/src/core/application.cpp index 4134ee89..10a69def 100644 --- a/src/core/application.cpp +++ b/src/core/application.cpp @@ -1015,6 +1015,7 @@ void Application::update(float deltaTime) { renderer->getCameraController()->setGravityDisabled(gameHandler->isGravityDisabled()); renderer->getCameraController()->setFeatherFallActive(gameHandler->isFeatherFalling()); renderer->getCameraController()->setWaterWalkActive(gameHandler->isWaterWalking()); + renderer->getCameraController()->setFlyingActive(gameHandler->isPlayerFlying()); } bool onTaxi = gameHandler && diff --git a/src/rendering/camera_controller.cpp b/src/rendering/camera_controller.cpp index 1617b0d0..5d536b33 100644 --- a/src/rendering/camera_controller.cpp +++ b/src/rendering/camera_controller.cpp @@ -692,6 +692,34 @@ void CameraController::update(float deltaTime) { } swimming = false; + // Player-controlled flight (flying mount / druid Flight Form): + // Use 3D pitch-following movement with no gravity or grounding. + if (flyingActive_) { + grounded = true; // suppress fall-damage checks + verticalVelocity = 0.0f; + jumpBufferTimer = 0.0f; + coyoteTimer = 0.0f; + + // Forward/back follows camera 3D direction (same as swim) + glm::vec3 flyFwd = glm::normalize(forward3D); + if (glm::length(flyFwd) < 1e-4f) flyFwd = forward; + glm::vec3 flyMove(0.0f); + if (nowForward) flyMove += flyFwd; + if (nowBackward) flyMove -= flyFwd; + if (nowStrafeLeft) flyMove += right; + if (nowStrafeRight) flyMove -= right; + // Space = ascend, X = descend while airborne + bool flyDescend = !uiWantsKeyboard && xDown && mounted_; + if (nowJump) flyMove.z += 1.0f; + if (flyDescend) flyMove.z -= 1.0f; + if (glm::length(flyMove) > 0.001f) { + flyMove = glm::normalize(flyMove); + targetPos += flyMove * speed * physicsDeltaTime; + } + targetPos.z += verticalVelocity * physicsDeltaTime; + // Skip all ground physics — go straight to collision/WMO sections + } else { + if (glm::length(movement) > 0.001f) { movement = glm::normalize(movement); targetPos += movement * speed * physicsDeltaTime; @@ -738,7 +766,8 @@ void CameraController::update(float deltaTime) { verticalVelocity = -2.0f; } targetPos.z += verticalVelocity * physicsDeltaTime; - } + } // end !flyingActive_ ground physics + } // end !inWater } else { // External follow (e.g., taxi): trust server position without grounding. swimming = false;