From 9a63720c72733ea1ec024751b85015a84f1ba6d7 Mon Sep 17 00:00:00 2001 From: wookieejedi Date: Fri, 21 Aug 2026 10:17:32 -0400 Subject: [PATCH] Fix do_view_track MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Root cause Commit 87ea04fa5 "change atan2_safe to atan2" (July 2024) replaced FSO's custom atan2_safe with the standard atan2. The old function returned a different range: range returned old atan2_safe (-PI/2, 3PI/2) standard atan2 (-PI, PI] do_view_track_target() in playercontrol.cpp:417 computes the padlock heading as a raw subtraction of two extracted headings: chase_slew_angles.h = forward_angles.h - view_angles.h; The forward vector rotated into the player's own frame is always (0,0,1), so forward_angles.h is always PI/2. With the old range that subtraction landed in (-PI, PI) — symmetric, and the ±120° neck clamp below applied evenly. With standard atan2 it lands in [-PI/2, 3PI/2) instead: -90° to the left, but +270° to the right. Leftward travel dead-ends at 90° (straight abeam) instead of 120°, and the moment a target crosses just past the player's left shoulder the value wraps to a large positive number and gets clamped to +120° — the camera snaps hard right. The fix Rather than reverting atan2_safe (standard atan2 is correct, and other callers depend on the new range), this PR wraps the heading difference back into (-PI, PI] before the neck clamp, using the same "over-the-top correction" idiom already used by do_view_external() a few hundred lines down: // Do over-the-top correction. // Headings are extracted with atan2, so each one lies in (-PI, PI] and their difference can be // nearly a full circle in either direction. Without wrapping it back into (-PI, PI], a target just // past the left shoulder reads as being almost all the way around to the right instead. if (chase_slew_angles.h > PI) chase_slew_angles.h -= PI2; else if (chase_slew_angles.h < -PI) chase_slew_angles.h += PI2; --- code/playerman/playercontrol.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/code/playerman/playercontrol.cpp b/code/playerman/playercontrol.cpp index 5d9894964be..05d85249ab2 100644 --- a/code/playerman/playercontrol.cpp +++ b/code/playerman/playercontrol.cpp @@ -417,6 +417,15 @@ void do_view_track_target() chase_slew_angles.h = forward_angles.h - view_angles.h; chase_slew_angles.p = -(forward_angles.p - view_angles.p); + // Do over-the-top correction. + // Headings are extracted with atan2, so each one lies in (-PI, PI] and their difference can be + // nearly a full circle in either direction. Without wrapping it back into (-PI, PI], a target just + // past the left shoulder reads as being almost all the way around to the right instead. + if (chase_slew_angles.h > PI) + chase_slew_angles.h -= PI2; + else if (chase_slew_angles.h < -PI) + chase_slew_angles.h += PI2; + // the gimbal limits of the player's virtual neck. // These nested ifs prevent the player from looking up and // down beyond 90 degree angles.