diff --git a/include/game/game_handler.hpp b/include/game/game_handler.hpp index 481a2381..dcf8538e 100644 --- a/include/game/game_handler.hpp +++ b/include/game/game_handler.hpp @@ -1160,6 +1160,9 @@ public: bool isGravityDisabled() const { return (movementInfo.flags & static_cast(MovementFlags::LEVITATING)) != 0; } + bool isFeatherFalling() const { + return (movementInfo.flags & static_cast(MovementFlags::FEATHER_FALL)) != 0; + } void dismount(); // Taxi / Flight Paths diff --git a/include/game/world_packets.hpp b/include/game/world_packets.hpp index c13659c3..07a22d23 100644 --- a/include/game/world_packets.hpp +++ b/include/game/world_packets.hpp @@ -395,6 +395,7 @@ enum class MovementFlags : uint32_t { ROOT = 0x00000800, FALLING = 0x00001000, FALLINGFAR = 0x00002000, + FEATHER_FALL = 0x00004000, // Slow fall / Parachute SWIMMING = 0x00200000, ASCENDING = 0x00400000, CAN_FLY = 0x00800000, diff --git a/include/rendering/camera_controller.hpp b/include/rendering/camera_controller.hpp index fc770d6f..b82630f4 100644 --- a/include/rendering/camera_controller.hpp +++ b/include/rendering/camera_controller.hpp @@ -97,6 +97,7 @@ public: void setMovementRooted(bool rooted) { movementRooted_ = rooted; } bool isMovementRooted() const { return movementRooted_; } void setGravityDisabled(bool disabled) { gravityDisabled_ = disabled; } + void setFeatherFallActive(bool active) { featherFallActive_ = active; } void setMounted(bool m) { mounted_ = m; } void setMountHeightOffset(float offset) { mountHeightOffset_ = offset; } void setExternalFollow(bool enabled) { externalFollow_ = enabled; } @@ -279,6 +280,8 @@ private: bool movementRooted_ = false; // Server-driven gravity disable (levitate/hover): skip gravity accumulation. bool gravityDisabled_ = false; + // Server-driven feather fall: cap downward velocity to slow-fall terminal. + bool featherFallActive_ = false; bool mounted_ = false; float mountHeightOffset_ = 0.0f; bool externalMoving_ = false; diff --git a/src/core/application.cpp b/src/core/application.cpp index 5eb188b3..d494ada2 100644 --- a/src/core/application.cpp +++ b/src/core/application.cpp @@ -1013,6 +1013,7 @@ void Application::update(float deltaTime) { renderer->getCameraController()->setSwimSpeedOverride(gameHandler->getServerSwimSpeed()); renderer->getCameraController()->setMovementRooted(gameHandler->isPlayerRooted()); renderer->getCameraController()->setGravityDisabled(gameHandler->isGravityDisabled()); + renderer->getCameraController()->setFeatherFallActive(gameHandler->isFeatherFalling()); } bool onTaxi = gameHandler && diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index 51fce186..44ab4bb7 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -2455,7 +2455,8 @@ void GameHandler::handlePacket(network::Packet& packet) { static_cast(MovementFlags::CAN_FLY), false); break; case Opcode::SMSG_MOVE_FEATHER_FALL: - handleForceMoveFlagChange(packet, "FEATHER_FALL", Opcode::CMSG_MOVE_FEATHER_FALL_ACK, 0, true); + handleForceMoveFlagChange(packet, "FEATHER_FALL", Opcode::CMSG_MOVE_FEATHER_FALL_ACK, + static_cast(MovementFlags::FEATHER_FALL), true); break; case Opcode::SMSG_MOVE_WATER_WALK: handleForceMoveFlagChange(packet, "WATER_WALK", Opcode::CMSG_MOVE_WATER_WALK_ACK, 0, true); @@ -5590,7 +5591,8 @@ void GameHandler::handlePacket(network::Packet& packet) { handleForceMoveFlagChange(packet, "LAND_WALK", Opcode::CMSG_MOVE_WATER_WALK_ACK, 0, false); break; case Opcode::SMSG_MOVE_NORMAL_FALL: - handleForceMoveFlagChange(packet, "NORMAL_FALL", Opcode::CMSG_MOVE_FEATHER_FALL_ACK, 0, false); + handleForceMoveFlagChange(packet, "NORMAL_FALL", Opcode::CMSG_MOVE_FEATHER_FALL_ACK, + static_cast(MovementFlags::FEATHER_FALL), false); break; case Opcode::SMSG_MOVE_SET_CAN_TRANSITION_BETWEEN_SWIM_AND_FLY: handleForceMoveFlagChange(packet, "SET_CAN_TRANSITION_SWIM_FLY", diff --git a/src/rendering/camera_controller.cpp b/src/rendering/camera_controller.cpp index 94eadaac..22887e83 100644 --- a/src/rendering/camera_controller.cpp +++ b/src/rendering/camera_controller.cpp @@ -726,6 +726,9 @@ void CameraController::update(float deltaTime) { else verticalVelocity *= std::max(0.0f, 1.0f - 3.0f * physicsDeltaTime); } else { verticalVelocity += gravity * physicsDeltaTime; + // Feather Fall / Slow Fall: cap downward terminal velocity to ~2 m/s + if (featherFallActive_ && verticalVelocity < -2.0f) + verticalVelocity = -2.0f; } targetPos.z += verticalVelocity * physicsDeltaTime; }