|
16 | 16 | #include <game/CPedDamageResponse.h> |
17 | 17 | #include <game/CEventList.h> |
18 | 18 | #include <game/CEventDamage.h> |
19 | | -#include "../game_sa/CCamSA.h" |
20 | 19 | #include <cmath> |
21 | 20 |
|
22 | 21 | class CEventDamageSAInterface; |
@@ -4899,6 +4898,81 @@ struct FollowCamLastGoodAngles |
4899 | 4898 | }; |
4900 | 4899 | static FollowCamLastGoodAngles s_lastGoodAngles{}; |
4901 | 4900 |
|
| 4901 | +static void RecoverFollowCamState(DWORD dwCam) |
| 4902 | +{ |
| 4903 | + auto badf = [](float v) { return !std::isfinite(v); }; |
| 4904 | + |
| 4905 | + const float rawBeta = *(float*)(dwCam + 0xBC); |
| 4906 | + const float rawTargetBeta = *(float*)(dwCam + 0x8C); |
| 4907 | + const float rawBetaSpd = *(float*)(dwCam + 0xC0); |
| 4908 | + const float rawAlpha = *(float*)(dwCam + 0xAC); |
| 4909 | + const float rawTrueBeta = *(float*)(dwCam + 0xA0); |
| 4910 | + |
| 4911 | + const bool badBeta = badf(rawBeta); |
| 4912 | + const bool badTargetBeta = badf(rawTargetBeta); |
| 4913 | + const bool badBetaSpd = badf(rawBetaSpd); |
| 4914 | + const bool badAlpha = badf(rawAlpha); |
| 4915 | + |
| 4916 | + if (!badBeta && !badTargetBeta && !badBetaSpd && !badAlpha) |
| 4917 | + { |
| 4918 | + s_lastGoodAngles = FollowCamLastGoodAngles{true, rawBeta, rawAlpha}; |
| 4919 | + return; |
| 4920 | + } |
| 4921 | + |
| 4922 | + // Prefer live SA values first, then a recent finite snapshot. |
| 4923 | + float safeBeta = rawBeta; |
| 4924 | + if (badf(safeBeta)) |
| 4925 | + { |
| 4926 | + if (std::isfinite(rawTrueBeta)) |
| 4927 | + safeBeta = rawTrueBeta; |
| 4928 | + else if (std::isfinite(rawTargetBeta)) |
| 4929 | + safeBeta = rawTargetBeta; |
| 4930 | + else if (s_lastGoodAngles.valid && std::isfinite(s_lastGoodAngles.beta)) |
| 4931 | + safeBeta = s_lastGoodAngles.beta; |
| 4932 | + else |
| 4933 | + safeBeta = 0.0f; |
| 4934 | + } |
| 4935 | + |
| 4936 | + float safeAlpha = rawAlpha; |
| 4937 | + if (badf(safeAlpha)) |
| 4938 | + { |
| 4939 | + if (s_lastGoodAngles.valid && std::isfinite(s_lastGoodAngles.alpha)) |
| 4940 | + safeAlpha = s_lastGoodAngles.alpha; |
| 4941 | + else |
| 4942 | + safeAlpha = 0.0f; |
| 4943 | + } |
| 4944 | + |
| 4945 | + // Only touch fields that are invalid. Keeping valid fields untouched avoids |
| 4946 | + // fighting SA's own follow-cam smoothing and mouse input handling. |
| 4947 | + if (badBeta) |
| 4948 | + *(float*)(dwCam + 0xBC) = safeBeta; |
| 4949 | + |
| 4950 | + if (badTargetBeta) |
| 4951 | + *(float*)(dwCam + 0x8C) = safeBeta; |
| 4952 | + |
| 4953 | + if (badBetaSpd) |
| 4954 | + *(float*)(dwCam + 0xC0) = 0.0f; |
| 4955 | + |
| 4956 | + if (badAlpha) |
| 4957 | + *(float*)(dwCam + 0xAC) = safeAlpha; |
| 4958 | + |
| 4959 | + if (std::isfinite(safeBeta) && std::isfinite(safeAlpha)) |
| 4960 | + s_lastGoodAngles = FollowCamLastGoodAngles{true, safeBeta, safeAlpha}; |
| 4961 | +} |
| 4962 | + |
| 4963 | +static void GetSafeFollowCamAngles(DWORD dwCam, float& outBeta, float& outAlpha) |
| 4964 | +{ |
| 4965 | + RecoverFollowCamState(dwCam); |
| 4966 | + |
| 4967 | + outBeta = *(float*)(dwCam + 0xBC); |
| 4968 | + outAlpha = *(float*)(dwCam + 0xAC); |
| 4969 | + |
| 4970 | + if (!std::isfinite(outBeta)) |
| 4971 | + outBeta = 0.0f; |
| 4972 | + if (!std::isfinite(outAlpha)) |
| 4973 | + outAlpha = 0.0f; |
| 4974 | +} |
| 4975 | + |
4902 | 4976 | bool _cdecl VehicleCamStart(DWORD dwCam, DWORD pVehicleInterface) |
4903 | 4977 | { |
4904 | 4978 | SClientEntity<CVehicleSA>* pVehicleClientEntity = pGameInterface->GetPools()->GetVehicle((DWORD*)pVehicleInterface); |
@@ -5020,61 +5094,15 @@ static void __declspec(naked) HOOK_VehicleCamLookDir1() |
5020 | 5094 |
|
5021 | 5095 | bool _cdecl VehicleCamLookDir2(DWORD dwCam) |
5022 | 5096 | { |
5023 | | - // Recover non-finite follow-camera angles before lookdir reconstruction |
5024 | | - // would propagate them into the camera direction vector. |
5025 | | - { |
5026 | | - auto badf = [](float v) { return !std::isfinite(v); }; |
5027 | | - |
5028 | | - const float rawBeta = *(float*)(dwCam + 0xBC); |
5029 | | - const float rawBetaSpd = *(float*)(dwCam + 0xC0); |
5030 | | - const float rawAlpha = *(float*)(dwCam + 0xAC); |
5031 | | - const bool badBeta = badf(rawBeta); |
5032 | | - const bool badBetaSpd = badf(rawBetaSpd); |
5033 | | - const bool badAlpha = badf(rawAlpha); |
5034 | | - const bool needRecover = badBeta || badBetaSpd || badAlpha; |
5035 | | - |
5036 | | - if (needRecover) |
5037 | | - { |
5038 | | - // Preserve live beta when only beta speed is invalid to avoid |
5039 | | - // snapping camera input to stale history. |
5040 | | - float fallbackBeta = std::isfinite(rawBeta) ? rawBeta : 0.0f; |
5041 | | - float fallbackAlpha = std::isfinite(rawAlpha) ? rawAlpha : 0.0f; |
5042 | | - |
5043 | | - if (!std::isfinite(rawBeta)) |
5044 | | - { |
5045 | | - const float trueBeta = *(float*)(dwCam + 0xA0); |
5046 | | - if (std::isfinite(trueBeta)) |
5047 | | - fallbackBeta = trueBeta; |
5048 | | - else if (s_lastGoodAngles.valid && std::isfinite(s_lastGoodAngles.beta)) |
5049 | | - fallbackBeta = s_lastGoodAngles.beta; |
5050 | | - } |
5051 | | - |
5052 | | - if (!std::isfinite(rawAlpha) && s_lastGoodAngles.valid && std::isfinite(s_lastGoodAngles.alpha)) |
5053 | | - fallbackAlpha = s_lastGoodAngles.alpha; |
5054 | | - |
5055 | | - *(float*)(dwCam + 0xBC) = fallbackBeta; // m_fHorizontalAngle (beta) |
5056 | | - if (badBetaSpd) |
5057 | | - *(float*)(dwCam + 0xC0) = 0.0f; // m_fBetaSpeed |
5058 | | - |
5059 | | - // Rewrite TargetBeta only when beta itself is invalid. |
5060 | | - if (badBeta) |
5061 | | - *(float*)(dwCam + 0x8C) = fallbackBeta; |
5062 | | - if (badAlpha) |
5063 | | - *(float*)(dwCam + 0xAC) = fallbackAlpha; |
5064 | | - } |
5065 | | - |
5066 | | - if (std::isfinite(rawBeta) && std::isfinite(rawAlpha)) |
5067 | | - { |
5068 | | - s_lastGoodAngles = FollowCamLastGoodAngles{true, rawBeta, rawAlpha}; |
5069 | | - } |
5070 | | - } |
| 5097 | + // Repair only invalid follow-cam internals, then use the sanitized angles |
| 5098 | + // for output reconstruction. |
| 5099 | + float fPhi = 0.0f; |
| 5100 | + float fTheta = 0.0f; |
| 5101 | + GetSafeFollowCamAngles(dwCam, fPhi, fTheta); |
5071 | 5102 |
|
5072 | 5103 | // Calculates the look direction vector for the vehicle camera. This vector |
5073 | 5104 | // is later multiplied by a factor and added to the vehicle position by SA |
5074 | 5105 | // to obtain the final camera position. |
5075 | | - float fPhi = *(float*)(dwCam + 0xBC); |
5076 | | - float fTheta = *(float*)(dwCam + 0xAC); |
5077 | | - |
5078 | 5106 | MemPutFast<CVector>(dwCam + 0x190, -gravcam_matGravity.vRight * cos(fPhi) * cos(fTheta) - gravcam_matGravity.vFront * sin(fPhi) * cos(fTheta) + |
5079 | 5107 | gravcam_matGravity.vUp * sin(fTheta)); |
5080 | 5108 |
|
@@ -5105,7 +5133,9 @@ static void __declspec(naked) HOOK_VehicleCamLookDir2() |
5105 | 5133 |
|
5106 | 5134 | void _cdecl VehicleCamHistory(DWORD dwCam, CVector* pvecTarget, float fTargetTheta, float fRadius, float fZoom) |
5107 | 5135 | { |
5108 | | - float fPhi = *(float*)(dwCam + 0xBC); |
| 5136 | + float fPhi = 0.0f; |
| 5137 | + float fAlphaIgnored = 0.0f; |
| 5138 | + GetSafeFollowCamAngles(dwCam, fPhi, fAlphaIgnored); |
5109 | 5139 |
|
5110 | 5140 | CVector vecDir = -gravcam_matGravity.vRight * cos(fPhi) * cos(fTargetTheta) - gravcam_matGravity.vFront * sin(fPhi) * cos(fTargetTheta) + |
5111 | 5141 | gravcam_matGravity.vUp * sin(fTargetTheta); |
|
0 commit comments