diff --git a/include/game/game_handler.hpp b/include/game/game_handler.hpp index 8b39d3d9..60afeb23 100644 --- a/include/game/game_handler.hpp +++ b/include/game/game_handler.hpp @@ -1155,6 +1155,9 @@ public: bool isPlayerRooted() const { return (movementInfo.flags & static_cast(MovementFlags::ROOT)) != 0; } + bool isGravityDisabled() const { + return (movementInfo.flags & static_cast(MovementFlags::LEVITATING)) != 0; + } void dismount(); // Taxi / Flight Paths diff --git a/include/rendering/camera_controller.hpp b/include/rendering/camera_controller.hpp index 2c8baf3a..d42ec5c0 100644 --- a/include/rendering/camera_controller.hpp +++ b/include/rendering/camera_controller.hpp @@ -94,6 +94,7 @@ public: void setRunSpeedOverride(float speed) { runSpeedOverride_ = speed; } void setMovementRooted(bool rooted) { movementRooted_ = rooted; } bool isMovementRooted() const { return movementRooted_; } + void setGravityDisabled(bool disabled) { gravityDisabled_ = disabled; } void setMounted(bool m) { mounted_ = m; } void setMountHeightOffset(float offset) { mountHeightOffset_ = offset; } void setExternalFollow(bool enabled) { externalFollow_ = enabled; } @@ -272,6 +273,8 @@ private: float runSpeedOverride_ = 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. + bool gravityDisabled_ = false; bool mounted_ = false; float mountHeightOffset_ = 0.0f; bool externalMoving_ = false; diff --git a/src/core/application.cpp b/src/core/application.cpp index 7de5e2d9..06c43ff5 100644 --- a/src/core/application.cpp +++ b/src/core/application.cpp @@ -1010,6 +1010,7 @@ void Application::update(float deltaTime) { if (renderer && gameHandler && renderer->getCameraController()) { renderer->getCameraController()->setRunSpeedOverride(gameHandler->getServerRunSpeed()); renderer->getCameraController()->setMovementRooted(gameHandler->isPlayerRooted()); + renderer->getCameraController()->setGravityDisabled(gameHandler->isGravityDisabled()); } bool onTaxi = gameHandler && diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index 5751f602..51fce186 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -5579,10 +5579,12 @@ void GameHandler::handlePacket(network::Packet& packet) { // ---- Player movement flag changes (server-pushed) ---- case Opcode::SMSG_MOVE_GRAVITY_DISABLE: - handleForceMoveFlagChange(packet, "GRAVITY_DISABLE", Opcode::CMSG_MOVE_GRAVITY_DISABLE_ACK, 0, true); + handleForceMoveFlagChange(packet, "GRAVITY_DISABLE", Opcode::CMSG_MOVE_GRAVITY_DISABLE_ACK, + static_cast(MovementFlags::LEVITATING), true); break; case Opcode::SMSG_MOVE_GRAVITY_ENABLE: - handleForceMoveFlagChange(packet, "GRAVITY_ENABLE", Opcode::CMSG_MOVE_GRAVITY_ENABLE_ACK, 0, true); + handleForceMoveFlagChange(packet, "GRAVITY_ENABLE", Opcode::CMSG_MOVE_GRAVITY_ENABLE_ACK, + static_cast(MovementFlags::LEVITATING), false); break; case Opcode::SMSG_MOVE_LAND_WALK: handleForceMoveFlagChange(packet, "LAND_WALK", Opcode::CMSG_MOVE_WATER_WALK_ACK, 0, false); diff --git a/src/rendering/camera_controller.cpp b/src/rendering/camera_controller.cpp index 1c5930b0..b8f4ce30 100644 --- a/src/rendering/camera_controller.cpp +++ b/src/rendering/camera_controller.cpp @@ -717,8 +717,14 @@ void CameraController::update(float deltaTime) { jumpBufferTimer -= physicsDeltaTime; coyoteTimer -= physicsDeltaTime; - // Apply gravity - verticalVelocity += gravity * physicsDeltaTime; + // Apply gravity (skip when server has disabled gravity, e.g. Levitate spell) + if (gravityDisabled_) { + // Float in place: bleed off any downward velocity, allow upward to decay slowly + if (verticalVelocity < 0.0f) verticalVelocity = 0.0f; + else verticalVelocity *= std::max(0.0f, 1.0f - 3.0f * physicsDeltaTime); + } else { + verticalVelocity += gravity * physicsDeltaTime; + } targetPos.z += verticalVelocity * physicsDeltaTime; } } else {