diff --git a/rts/Sim/MoveTypes/GroundMoveType.cpp b/rts/Sim/MoveTypes/GroundMoveType.cpp index 3b5b016d04..f74a1b92e8 100644 --- a/rts/Sim/MoveTypes/GroundMoveType.cpp +++ b/rts/Sim/MoveTypes/GroundMoveType.cpp @@ -3322,8 +3322,9 @@ bool CGroundMoveType::UpdateDirectControl() ChangeSpeed(0.0f, false, true); } - if (unitCon.left ) { ChangeHeading(owner->heading + turnRate); turnSign = 1.0f; } - if (unitCon.right) { ChangeHeading(owner->heading - turnRate); turnSign = -1.0f; } + const short unitTurnRate = FloatToHeading(turnRate); + if (unitCon.left ) { ChangeHeading(owner->heading + unitTurnRate); turnSign = 1.0f; } + if (unitCon.right) { ChangeHeading(owner->heading - unitTurnRate); turnSign = -1.0f; } // local client is controlling us if (selfCon.GetControllee() == owner) diff --git a/rts/Sim/Path/IPathController.cpp b/rts/Sim/Path/IPathController.cpp index 060cad120b..5af1499bdf 100644 --- a/rts/Sim/Path/IPathController.cpp +++ b/rts/Sim/Path/IPathController.cpp @@ -79,7 +79,8 @@ short GMTDefaultPathController::GetDeltaHeading( // add lookahead term to avoid overshooting target heading // note that turnBrakeDist is always positive const short brakeDistFactor = (absTurnSpeed >= maxTurnAccel); - const short stopTurnHeading = oldHeading + (turnBrakeDist * Sign(curTurnSpeed) * brakeDistFactor); + const short brakeDist = FloatToHeading(turnBrakeDist * Sign(curTurnSpeed)); + const short stopTurnHeading = oldHeading + (brakeDist * brakeDistFactor); const short curDeltaHeading = newHeading - stopTurnHeading; if (brakeDistFactor == 0) { diff --git a/rts/System/SpringMath.h b/rts/System/SpringMath.h index f49871c778..3f6a2c91a1 100644 --- a/rts/System/SpringMath.h +++ b/rts/System/SpringMath.h @@ -52,6 +52,7 @@ short int GetHeadingFromFacing(const int facing) _pure _warn_unused_result; int GetFacingFromHeading(const short int heading) _pure _warn_unused_result; float GetHeadingFromVectorF(const float dx, const float dz) _pure _warn_unused_result; short int GetHeadingFromVector(const float dx, const float dz) _pure _warn_unused_result; +short int FloatToHeading(const float angle) _pure _warn_unused_result; shortint2 GetHAndPFromVector(const float3 vec) _pure _warn_unused_result; // vec should be normalized float2 GetHAndPFromVectorF(const float3 vec) _pure _warn_unused_result; // vec should be normalized float3 GetVectorFromHeading(const short int heading) _pure _warn_unused_result; diff --git a/rts/System/SpringMath.inl b/rts/System/SpringMath.inl index 45695880c1..7c9bf471c0 100644 --- a/rts/System/SpringMath.inl +++ b/rts/System/SpringMath.inl @@ -22,6 +22,17 @@ inline short int GetHeadingFromFacing(const int facing) } } +// headings are circular 16-bit integer angles, so sums of them are meant to wrap. +// float->short is UB once out of range, so the float side has to be in range +// already; the assert catches a caller that is not in debug builds. +inline short int FloatToHeading(const float angle) +{ + assert(angle >= static_cast(std::numeric_limits::min()) + && angle <= static_cast(std::numeric_limits::max())); + + return static_cast(angle); +} + inline int GetFacingFromHeading(const short int heading) { if (heading >= 0) {