From 7be84575d34550c1386bb6ad61e249d81dae2c33 Mon Sep 17 00:00:00 2001 From: xezon <4720891+xezon@users.noreply.github.com> Date: Wed, 22 Jul 2026 19:44:57 +0200 Subject: [PATCH 1/3] bugfix(physics): Fix diagonal movement speed discrepancy --- Core/GameEngine/Include/Common/GameDefines.h | 4 +++ .../GameLogic/Object/Update/PhysicsUpdate.cpp | 25 ++++++++++++++++--- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/Core/GameEngine/Include/Common/GameDefines.h b/Core/GameEngine/Include/Common/GameDefines.h index 7a6f4be4d11..07a3dee5de7 100644 --- a/Core/GameEngine/Include/Common/GameDefines.h +++ b/Core/GameEngine/Include/Common/GameDefines.h @@ -87,6 +87,10 @@ #define PRESERVE_RETAIL_SCRIPTED_CAMERA (1) // Retain scripted camera behavior present in retail Generals 1.08 and Zero Hour 1.04 #endif +#ifndef PRESERVE_RETAIL_PHYSICS_FORWARD_SPEED +#define PRESERVE_RETAIL_PHYSICS_FORWARD_SPEED (1) +#endif + #ifndef RETAIL_COMPATIBLE_CRC #define RETAIL_COMPATIBLE_CRC (1) // Game is expected to be CRC compatible with retail Generals 1.08, Zero Hour 1.04 #endif diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/PhysicsUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/PhysicsUpdate.cpp index 78b256e5b88..b3705178c6a 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/PhysicsUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/PhysicsUpdate.cpp @@ -963,15 +963,22 @@ Real PhysicsBehavior::getForwardSpeed2D() const Real dot = vx + vy; - Real speedSquared = vx*vx + vy*vy; -// DEBUG_ASSERTCRASH( speedSquared != 0, ("zero speedSquared will overflow sqrtf()!") );// lorenzen... sanity check - - Real speed = (Real)sqrtf( speedSquared ); +#if RETAIL_COMPATIBLE_CRC || PRESERVE_RETAIL_PHYSICS_FORWARD_SPEED + Real speed = (Real)sqrtf( vx*vx + vy*vy ); if (dot >= 0.0f) return speed; return -speed; +#else + // Inverse scale len by (1 + sqrt(2)) / 2 to adjust to the average of the former min/max movement speed. + // The inverse looks intuitively wrong, but it is correct, because the value returned by this function is + // used to determine the additional velocity needed to reach the target speed. + constexpr const Real DiagonalCompensation = 1.0f / 1.20710678f; + dot *= DiagonalCompensation; + + return dot; +#endif } //------------------------------------------------------------------------------------------------- @@ -989,12 +996,22 @@ Real PhysicsBehavior::getForwardSpeed3D() const Real dot = vx + vy + vz; +#if RETAIL_COMPATIBLE_CRC || PRESERVE_RETAIL_PHYSICS_FORWARD_SPEED Real speed = (Real)sqrtf( vx*vx + vy*vy + vz*vz ); if (dot >= 0.0f) return speed; return -speed; +#else + // Inverse scale len by (1 + sqrt(3)) / 2 to adjust to the average of the former min/max movement speed. + // The inverse looks intuitively wrong, but it is correct, because the value returned by this function is + // used to determine the additional velocity needed to reach the target speed. + constexpr const Real DiagonalCompensation = 1.0f / 1.36602540f; + dot *= DiagonalCompensation; + + return dot; +#endif } //------------------------------------------------------------------------------------------------- From 6f4def0bdc602d74bd7d9c05236bb2872c306b06 Mon Sep 17 00:00:00 2001 From: xezon <4720891+xezon@users.noreply.github.com> Date: Wed, 29 Jul 2026 21:50:33 +0200 Subject: [PATCH 2/3] Preserve legacy speeds for scripted movements --- Core/GameEngine/Include/Common/GameDefines.h | 7 +++ .../GameLogic/Object/Update/PhysicsUpdate.cpp | 46 ++++++++++++++----- 2 files changed, 41 insertions(+), 12 deletions(-) diff --git a/Core/GameEngine/Include/Common/GameDefines.h b/Core/GameEngine/Include/Common/GameDefines.h index 07a3dee5de7..1c87cd72938 100644 --- a/Core/GameEngine/Include/Common/GameDefines.h +++ b/Core/GameEngine/Include/Common/GameDefines.h @@ -87,10 +87,17 @@ #define PRESERVE_RETAIL_SCRIPTED_CAMERA (1) // Retain scripted camera behavior present in retail Generals 1.08 and Zero Hour 1.04 #endif +// Whether to preserve the 1.41x speed discrepancy between straight and diagonal movements of all objects. #ifndef PRESERVE_RETAIL_PHYSICS_FORWARD_SPEED #define PRESERVE_RETAIL_PHYSICS_FORWARD_SPEED (1) #endif +// Whether to preserve the 1.41x speed discrepancy between straight and diagonal movements of all scripted objects. +// This setting is very relevant for legacy missions and cinematic sequences. +#ifndef PRESERVE_RETAIL_SCRIPTED_PHYSICS_FORWARD_SPEED +#define PRESERVE_RETAIL_SCRIPTED_PHYSICS_FORWARD_SPEED (1) +#endif + #ifndef RETAIL_COMPATIBLE_CRC #define RETAIL_COMPATIBLE_CRC (1) // Game is expected to be CRC compatible with retail Generals 1.08, Zero Hour 1.04 #endif diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/PhysicsUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/PhysicsUpdate.cpp index b3705178c6a..8c9085a3e62 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/PhysicsUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/PhysicsUpdate.cpp @@ -957,27 +957,38 @@ Real PhysicsBehavior::getVelocityMagnitude() const Real PhysicsBehavior::getForwardSpeed2D() const { const Coord3D *dir = getObject()->getUnitDirectionVector2D(); - Real vx = m_vel.x * dir->x; Real vy = m_vel.y * dir->y; - Real dot = vx + vy; #if RETAIL_COMPATIBLE_CRC || PRESERVE_RETAIL_PHYSICS_FORWARD_SPEED - Real speed = (Real)sqrtf( vx*vx + vy*vy ); + Real speed = (Real)sqrtf( vx*vx + vy*vy ); if (dot >= 0.0f) return speed; - return -speed; + #else + +#if PRESERVE_RETAIL_SCRIPTED_PHYSICS_FORWARD_SPEED + if (const AIUpdateInterface *ai = getObject()->getAIUpdateInterface()) + { + if (ai->getLastCommandSource() == CMD_FROM_SCRIPT) + { + Real speed = (Real)sqrtf( vx*vx + vy*vy ); + if (dot >= 0.0f) + return speed; + return -speed; + } + } +#endif + // Inverse scale len by (1 + sqrt(2)) / 2 to adjust to the average of the former min/max movement speed. // The inverse looks intuitively wrong, but it is correct, because the value returned by this function is // used to determine the additional velocity needed to reach the target speed. constexpr const Real DiagonalCompensation = 1.0f / 1.20710678f; - dot *= DiagonalCompensation; + return dot * DiagonalCompensation; - return dot; #endif } @@ -989,28 +1000,39 @@ Real PhysicsBehavior::getForwardSpeed2D() const Real PhysicsBehavior::getForwardSpeed3D() const { Vector3 dir = getObject()->getTransformMatrix()->Get_X_Vector(); - Real vx = m_vel.x * dir.X; Real vy = m_vel.y * dir.Y; Real vz = m_vel.z * dir.Z; - Real dot = vx + vy + vz; #if RETAIL_COMPATIBLE_CRC || PRESERVE_RETAIL_PHYSICS_FORWARD_SPEED - Real speed = (Real)sqrtf( vx*vx + vy*vy + vz*vz ); + Real speed = (Real)sqrtf( vx*vx + vy*vy + vz*vz ); if (dot >= 0.0f) return speed; - return -speed; + #else + +#if PRESERVE_RETAIL_SCRIPTED_PHYSICS_FORWARD_SPEED + if (const AIUpdateInterface *ai = getObject()->getAIUpdateInterface()) + { + if (ai->getLastCommandSource() == CMD_FROM_SCRIPT) + { + Real speed = (Real)sqrtf( vx*vx + vy*vy + vz*vz ); + if (dot >= 0.0f) + return speed; + return -speed; + } + } +#endif + // Inverse scale len by (1 + sqrt(3)) / 2 to adjust to the average of the former min/max movement speed. // The inverse looks intuitively wrong, but it is correct, because the value returned by this function is // used to determine the additional velocity needed to reach the target speed. constexpr const Real DiagonalCompensation = 1.0f / 1.36602540f; - dot *= DiagonalCompensation; + return dot * DiagonalCompensation; - return dot; #endif } From 19ada8952d54458aff7ab56c4787bc373c93857e Mon Sep 17 00:00:00 2001 From: xezon <4720891+xezon@users.noreply.github.com> Date: Thu, 30 Jul 2026 22:20:22 +0200 Subject: [PATCH 3/3] Polish comments --- .../Source/GameLogic/Object/Update/PhysicsUpdate.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/PhysicsUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/PhysicsUpdate.cpp index 8c9085a3e62..d9088620c7d 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/PhysicsUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/PhysicsUpdate.cpp @@ -983,7 +983,8 @@ Real PhysicsBehavior::getForwardSpeed2D() const } #endif - // Inverse scale len by (1 + sqrt(2)) / 2 to adjust to the average of the former min/max movement speed. + // TheSuperHackers @bugfix xezon 30/07/2026 Now returns scaled dot product instead of +-sqrtf(vx*vx+vy*vy) + // Inverse scales len by (1 + sqrt(2)) / 2 to adjust to the average of the former min/max movement speed. // The inverse looks intuitively wrong, but it is correct, because the value returned by this function is // used to determine the additional velocity needed to reach the target speed. constexpr const Real DiagonalCompensation = 1.0f / 1.20710678f; @@ -1027,7 +1028,8 @@ Real PhysicsBehavior::getForwardSpeed3D() const } #endif - // Inverse scale len by (1 + sqrt(3)) / 2 to adjust to the average of the former min/max movement speed. + // TheSuperHackers @bugfix xezon 30/07/2026 Now returns scaled dot product instead of +-sqrtf(vx*vx+vy*vy+vz*vz) + // Inverse scales len by (1 + sqrt(3)) / 2 to adjust to the average of the former min/max movement speed. // The inverse looks intuitively wrong, but it is correct, because the value returned by this function is // used to determine the additional velocity needed to reach the target speed. constexpr const Real DiagonalCompensation = 1.0f / 1.36602540f;