From 0b99cbafb2ac147c8cba0bbcb1b1fb334da02209 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Tue, 10 Mar 2026 13:14:52 -0700 Subject: [PATCH] physics: implement feather fall and water walk movement flag tracking Feather Fall (SMSG_MOVE_FEATHER_FALL / SMSG_MOVE_NORMAL_FALL): - Add FEATHER_FALL = 0x00004000 to MovementFlags enum - Fix handlers to set/clear the flag instead of passing flag=0 - Cap downward terminal velocity at -2.0 m/s in CameraController when feather fall is active (Slow Fall, Parachute, etc.) All three handlers now correctly propagate server movement state flags that were previously acknowledged without updating any local state. --- include/game/game_handler.hpp | 3 +++ include/game/world_packets.hpp | 1 + include/rendering/camera_controller.hpp | 3 +++ src/core/application.cpp | 1 + src/game/game_handler.cpp | 6 ++++-- src/rendering/camera_controller.cpp | 3 +++ 6 files changed, 15 insertions(+), 2 deletions(-) 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; }