From d27516c9fb78ba578a694e3c2e5a81f966ea81d4 Mon Sep 17 00:00:00 2001 From: Bobby Battista Date: Fri, 19 Dec 2025 16:40:53 -0500 Subject: [PATCH 1/7] fix(input): Convert double-click fast drive timing from frames to real-time milliseconds --- .../Module/ParticleUplinkCannonUpdate.h | 8 ++- .../Update/ParticleUplinkCannonUpdate.cpp | 57 +++++++++++++++++-- 2 files changed, 57 insertions(+), 8 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/ParticleUplinkCannonUpdate.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/ParticleUplinkCannonUpdate.h index 015813b9a7..e25add874d 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/ParticleUplinkCannonUpdate.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/ParticleUplinkCannonUpdate.h @@ -227,8 +227,12 @@ class ParticleUplinkCannonUpdate : public SpecialPowerUpdateModule UnsignedInt m_nextDamagePulseFrame; UnsignedInt m_startAttackFrame; UnsignedInt m_startDecayFrame; - UnsignedInt m_lastDrivingClickFrame; - UnsignedInt m_2ndLastDrivingClickFrame; +#if RETAIL_COMPATIBLE_XFER_SAVE + UnsignedInt m_lastDrivingClickFrame; // Frame number for retail compatibility + UnsignedInt m_2ndLastDrivingClickFrame; // Frame number for retail compatibility +#endif + UnsignedInt m_lastDrivingClickTimeMsec; // Real-time milliseconds + UnsignedInt m_2ndLastDrivingClickTimeMsec; // Real-time milliseconds UnsignedInt m_nextDestWaypointID; Bool m_upBonesCached; diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp index d2d1d07928..0a1b2854ef 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp @@ -184,8 +184,12 @@ ParticleUplinkCannonUpdate::ParticleUplinkCannonUpdate( Thing *thing, const Modu m_nextDamagePulseFrame = 0; m_startAttackFrame = 0; m_startDecayFrame = 0; +#if RETAIL_COMPATIBLE_XFER_SAVE m_lastDrivingClickFrame = 0; m_2ndLastDrivingClickFrame = 0; +#endif + m_lastDrivingClickTimeMsec = 0; + m_2ndLastDrivingClickTimeMsec = 0; m_clientShroudedLastFrame = FALSE; for( Int i = 0; i < MAX_OUTER_NODES; i++ ) @@ -374,8 +378,12 @@ void ParticleUplinkCannonUpdate::setSpecialPowerOverridableDestination( const Co { m_overrideTargetDestination = *loc; m_manualTargetMode = TRUE; +#if RETAIL_COMPATIBLE_XFER_SAVE m_2ndLastDrivingClickFrame = m_lastDrivingClickFrame; m_lastDrivingClickFrame = TheGameLogic->getFrame(); +#endif + m_2ndLastDrivingClickTimeMsec = m_lastDrivingClickTimeMsec; + m_lastDrivingClickTimeMsec = timeGetTime(); } } @@ -567,7 +575,11 @@ UpdateSleepTime ParticleUplinkCannonUpdate::update() else { Real speed = data->m_manualDrivingSpeed; - if( m_scriptedWaypointMode || m_lastDrivingClickFrame - m_2ndLastDrivingClickFrame < data->m_doubleClickToFastDriveDelay ) +#if !RETAIL_COMPATIBLE_CRC + if( m_scriptedWaypointMode || (m_lastDrivingClickTimeMsec != 0 && m_2ndLastDrivingClickTimeMsec != 0 && m_lastDrivingClickTimeMsec - m_2ndLastDrivingClickTimeMsec < data->m_doubleClickToFastDriveDelay) ) +#else + if( m_scriptedWaypointMode || (m_lastDrivingClickFrame - m_2ndLastDrivingClickFrame < data->m_doubleClickToFastDriveDelay) ) +#endif { //Because we double clicked, use the faster driving speed. speed = data->m_manualFastDrivingSpeed; @@ -1392,7 +1404,12 @@ void ParticleUplinkCannonUpdate::crc( Xfer *xfer ) // ------------------------------------------------------------------------------------------------ /** Xfer method * Version Info: - * 1: Initial version */ + * 1: Initial version + * 2: Added m_startDecayFrame + * 3: Added m_manualTargetMode, m_scriptedWaypointMode, m_nextDestWaypointID + * 4: Added m_orbitToTargetLaserRadius + * 5: Changed m_lastDrivingClickFrame and m_2ndLastDrivingClickFrame to m_lastDrivingClickTimeMsec and m_2ndLastDrivingClickTimeMsec (frames to milliseconds) + */ // ------------------------------------------------------------------------------------------------ void ParticleUplinkCannonUpdate::xfer( Xfer *xfer ) { @@ -1402,7 +1419,7 @@ void ParticleUplinkCannonUpdate::xfer( Xfer *xfer ) #if RETAIL_COMPATIBLE_XFER_SAVE const XferVersion currentVersion = 3; #else - const XferVersion currentVersion = 4; + const XferVersion currentVersion = 5; #endif XferVersion version = currentVersion; xfer->xferVersion( &version, currentVersion ); @@ -1498,11 +1515,39 @@ void ParticleUplinkCannonUpdate::xfer( Xfer *xfer ) m_startDecayFrame = m_startAttackFrame + data->m_totalFiringFrames; } - // the time of last manual target click + // the time of last manual target click (milliseconds) +#if RETAIL_COMPATIBLE_XFER_SAVE + // Retail builds stay at version 3 for compatibility, so always use old frame format xfer->xferUnsignedInt( & m_lastDrivingClickFrame ); - - // the time of the 2nd last manual target click xfer->xferUnsignedInt( &m_2ndLastDrivingClickFrame ); +#if !RETAIL_COMPATIBLE_CRC + if( xfer->getXferMode() == XFER_LOAD ) + { + // Can't convert frames to milliseconds accurately, so set to 0 + m_lastDrivingClickTimeMsec = 0; + m_2ndLastDrivingClickTimeMsec = 0; + } +#endif +#else + if( version >= 5 ) + { + xfer->xferUnsignedInt( & m_lastDrivingClickTimeMsec ); + xfer->xferUnsignedInt( &m_2ndLastDrivingClickTimeMsec ); + } + else + { + // Old versions stored frame numbers, which we can't meaningfully convert to milliseconds + UnsignedInt oldLastDrivingClickFrame = 0; + UnsignedInt old2ndLastDrivingClickFrame = 0; + xfer->xferUnsignedInt( &oldLastDrivingClickFrame ); + xfer->xferUnsignedInt( &old2ndLastDrivingClickFrame ); + if( xfer->getXferMode() == XFER_LOAD ) + { + m_lastDrivingClickTimeMsec = 0; + m_2ndLastDrivingClickTimeMsec = 0; + } + } +#endif if( version >= 3 ) { From 2e261bdaea28c66af1a3fff102a9663dfb358a81 Mon Sep 17 00:00:00 2001 From: Bobby Battista Date: Sat, 20 Dec 2025 15:17:06 -0500 Subject: [PATCH 2/7] refactor(input): Extract double-click condition to variable in ParticleUplinkCannon --- .../GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp | 3 ++- .../GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Generals/Code/GameEngine/Source/GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp b/Generals/Code/GameEngine/Source/GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp index ad7614d1f1..e79498d3e6 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp @@ -517,7 +517,8 @@ UpdateSleepTime ParticleUplinkCannonUpdate::update() else { Real speed = data->m_manualDrivingSpeed; - if( m_lastDrivingClickFrame - m_2ndLastDrivingClickFrame < data->m_doubleClickToFastDriveDelay ) + const Bool useFasterSpeed = m_lastDrivingClickFrame - m_2ndLastDrivingClickFrame < data->m_doubleClickToFastDriveDelay; + if( useFasterSpeed ) { //Because we double clicked, use the faster driving speed. speed = data->m_manualFastDrivingSpeed; diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp index 0a1b2854ef..246e59c75d 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp @@ -576,10 +576,11 @@ UpdateSleepTime ParticleUplinkCannonUpdate::update() { Real speed = data->m_manualDrivingSpeed; #if !RETAIL_COMPATIBLE_CRC - if( m_scriptedWaypointMode || (m_lastDrivingClickTimeMsec != 0 && m_2ndLastDrivingClickTimeMsec != 0 && m_lastDrivingClickTimeMsec - m_2ndLastDrivingClickTimeMsec < data->m_doubleClickToFastDriveDelay) ) + const Bool useFasterSpeed = m_scriptedWaypointMode || (m_lastDrivingClickTimeMsec != 0 && m_2ndLastDrivingClickTimeMsec != 0 && m_lastDrivingClickTimeMsec - m_2ndLastDrivingClickTimeMsec < data->m_doubleClickToFastDriveDelay); #else - if( m_scriptedWaypointMode || (m_lastDrivingClickFrame - m_2ndLastDrivingClickFrame < data->m_doubleClickToFastDriveDelay) ) + const Bool useFasterSpeed = m_scriptedWaypointMode || (m_lastDrivingClickFrame - m_2ndLastDrivingClickFrame < data->m_doubleClickToFastDriveDelay); #endif + if( useFasterSpeed ) { //Because we double clicked, use the faster driving speed. speed = data->m_manualFastDrivingSpeed; From 9a758f41c88be64cdb84d1465dc1272149b9c391 Mon Sep 17 00:00:00 2001 From: Bobby Battista Date: Sat, 20 Dec 2025 15:17:43 -0500 Subject: [PATCH 3/7] style: Fix spacing inconsistency in ParticleUplinkCannon xfer function --- .../GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp | 2 +- .../GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Generals/Code/GameEngine/Source/GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp b/Generals/Code/GameEngine/Source/GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp index e79498d3e6..ac6c94746f 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp @@ -1416,7 +1416,7 @@ void ParticleUplinkCannonUpdate::xfer( Xfer *xfer ) } // the time of last manual target click - xfer->xferUnsignedInt( & m_lastDrivingClickFrame ); + xfer->xferUnsignedInt( &m_lastDrivingClickFrame ); // the time of the 2nd last manual target click xfer->xferUnsignedInt( &m_2ndLastDrivingClickFrame ); diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp index 246e59c75d..cecf009e6f 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp @@ -1519,7 +1519,7 @@ void ParticleUplinkCannonUpdate::xfer( Xfer *xfer ) // the time of last manual target click (milliseconds) #if RETAIL_COMPATIBLE_XFER_SAVE // Retail builds stay at version 3 for compatibility, so always use old frame format - xfer->xferUnsignedInt( & m_lastDrivingClickFrame ); + xfer->xferUnsignedInt( &m_lastDrivingClickFrame ); xfer->xferUnsignedInt( &m_2ndLastDrivingClickFrame ); #if !RETAIL_COMPATIBLE_CRC if( xfer->getXferMode() == XFER_LOAD ) @@ -1532,7 +1532,7 @@ void ParticleUplinkCannonUpdate::xfer( Xfer *xfer ) #else if( version >= 5 ) { - xfer->xferUnsignedInt( & m_lastDrivingClickTimeMsec ); + xfer->xferUnsignedInt( &m_lastDrivingClickTimeMsec ); xfer->xferUnsignedInt( &m_2ndLastDrivingClickTimeMsec ); } else From 1cc6468e9d9133d41f35cab1814acc0d9eaac67c Mon Sep 17 00:00:00 2001 From: Bobby Battista Date: Sat, 20 Dec 2025 15:18:18 -0500 Subject: [PATCH 4/7] refactor: Make millisecond variables conditional in ParticleUplinkCannon retail builds --- .../Module/ParticleUplinkCannonUpdate.h | 7 +++--- .../Update/ParticleUplinkCannonUpdate.cpp | 22 +++++++------------ 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/ParticleUplinkCannonUpdate.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/ParticleUplinkCannonUpdate.h index e25add874d..a75261b948 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/ParticleUplinkCannonUpdate.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/ParticleUplinkCannonUpdate.h @@ -227,12 +227,13 @@ class ParticleUplinkCannonUpdate : public SpecialPowerUpdateModule UnsignedInt m_nextDamagePulseFrame; UnsignedInt m_startAttackFrame; UnsignedInt m_startDecayFrame; -#if RETAIL_COMPATIBLE_XFER_SAVE - UnsignedInt m_lastDrivingClickFrame; // Frame number for retail compatibility +#if RETAIL_COMPATIBLE_CRC || RETAIL_COMPATIBLE_XFER_SAVE + UnsignedInt m_lastDrivingClickFrame; // Frame number for retail compatibility UnsignedInt m_2ndLastDrivingClickFrame; // Frame number for retail compatibility -#endif +#else UnsignedInt m_lastDrivingClickTimeMsec; // Real-time milliseconds UnsignedInt m_2ndLastDrivingClickTimeMsec; // Real-time milliseconds +#endif UnsignedInt m_nextDestWaypointID; Bool m_upBonesCached; diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp index cecf009e6f..16f6d4f722 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp @@ -184,12 +184,13 @@ ParticleUplinkCannonUpdate::ParticleUplinkCannonUpdate( Thing *thing, const Modu m_nextDamagePulseFrame = 0; m_startAttackFrame = 0; m_startDecayFrame = 0; -#if RETAIL_COMPATIBLE_XFER_SAVE +#if RETAIL_COMPATIBLE_CRC || RETAIL_COMPATIBLE_XFER_SAVE m_lastDrivingClickFrame = 0; m_2ndLastDrivingClickFrame = 0; -#endif +#else m_lastDrivingClickTimeMsec = 0; m_2ndLastDrivingClickTimeMsec = 0; +#endif m_clientShroudedLastFrame = FALSE; for( Int i = 0; i < MAX_OUTER_NODES; i++ ) @@ -378,12 +379,13 @@ void ParticleUplinkCannonUpdate::setSpecialPowerOverridableDestination( const Co { m_overrideTargetDestination = *loc; m_manualTargetMode = TRUE; -#if RETAIL_COMPATIBLE_XFER_SAVE +#if RETAIL_COMPATIBLE_CRC || RETAIL_COMPATIBLE_XFER_SAVE m_2ndLastDrivingClickFrame = m_lastDrivingClickFrame; m_lastDrivingClickFrame = TheGameLogic->getFrame(); -#endif +#else m_2ndLastDrivingClickTimeMsec = m_lastDrivingClickTimeMsec; m_lastDrivingClickTimeMsec = timeGetTime(); +#endif } } @@ -1516,19 +1518,11 @@ void ParticleUplinkCannonUpdate::xfer( Xfer *xfer ) m_startDecayFrame = m_startAttackFrame + data->m_totalFiringFrames; } - // the time of last manual target click (milliseconds) -#if RETAIL_COMPATIBLE_XFER_SAVE + // the time of last manual target click +#if RETAIL_COMPATIBLE_CRC || RETAIL_COMPATIBLE_XFER_SAVE // Retail builds stay at version 3 for compatibility, so always use old frame format xfer->xferUnsignedInt( &m_lastDrivingClickFrame ); xfer->xferUnsignedInt( &m_2ndLastDrivingClickFrame ); -#if !RETAIL_COMPATIBLE_CRC - if( xfer->getXferMode() == XFER_LOAD ) - { - // Can't convert frames to milliseconds accurately, so set to 0 - m_lastDrivingClickTimeMsec = 0; - m_2ndLastDrivingClickTimeMsec = 0; - } -#endif #else if( version >= 5 ) { From c63aab5163b710f255ee1e651e899d3e8bbd0b96 Mon Sep 17 00:00:00 2001 From: Bobby Battista Date: Sun, 21 Dec 2025 14:30:05 -0500 Subject: [PATCH 5/7] fix(input): Fix double-click fast drive timing in ParticleUplinkCannon --- .../GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp index 16f6d4f722..925f047b6f 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp @@ -577,10 +577,10 @@ UpdateSleepTime ParticleUplinkCannonUpdate::update() else { Real speed = data->m_manualDrivingSpeed; -#if !RETAIL_COMPATIBLE_CRC - const Bool useFasterSpeed = m_scriptedWaypointMode || (m_lastDrivingClickTimeMsec != 0 && m_2ndLastDrivingClickTimeMsec != 0 && m_lastDrivingClickTimeMsec - m_2ndLastDrivingClickTimeMsec < data->m_doubleClickToFastDriveDelay); +#if RETAIL_COMPATIBLE_CRC + const Bool useFasterSpeed = m_scriptedWaypointMode || (m_lastDrivingClickFrame != 0 && m_2ndLastDrivingClickFrame != 0 && m_lastDrivingClickFrame - m_2ndLastDrivingClickFrame < data->m_doubleClickToFastDriveDelay); #else - const Bool useFasterSpeed = m_scriptedWaypointMode || (m_lastDrivingClickFrame - m_2ndLastDrivingClickFrame < data->m_doubleClickToFastDriveDelay); + const Bool useFasterSpeed = m_scriptedWaypointMode || (m_lastDrivingClickTimeMsec != 0 && m_2ndLastDrivingClickTimeMsec != 0 && m_lastDrivingClickTimeMsec - m_2ndLastDrivingClickTimeMsec < data->m_doubleClickToFastDriveDelay); #endif if( useFasterSpeed ) { From d3e0024b5fb6b2f8e8b0d3bba05529e8404f942a Mon Sep 17 00:00:00 2001 From: Bobby Battista Date: Sun, 21 Dec 2025 14:37:01 -0500 Subject: [PATCH 6/7] Replicate to generals --- .../Module/ParticleUplinkCannonUpdate.h | 9 ++- .../Update/ParticleUplinkCannonUpdate.cpp | 56 +++++++++++++++++-- 2 files changed, 57 insertions(+), 8 deletions(-) diff --git a/Generals/Code/GameEngine/Include/GameLogic/Module/ParticleUplinkCannonUpdate.h b/Generals/Code/GameEngine/Include/GameLogic/Module/ParticleUplinkCannonUpdate.h index f313a06f55..4ed3bda71c 100644 --- a/Generals/Code/GameEngine/Include/GameLogic/Module/ParticleUplinkCannonUpdate.h +++ b/Generals/Code/GameEngine/Include/GameLogic/Module/ParticleUplinkCannonUpdate.h @@ -225,8 +225,13 @@ class ParticleUplinkCannonUpdate : public UpdateModule, public SpecialPowerUpdat UnsignedInt m_nextDamagePulseFrame; UnsignedInt m_startAttackFrame; UnsignedInt m_startDecayFrame; - UnsignedInt m_lastDrivingClickFrame; - UnsignedInt m_2ndLastDrivingClickFrame; +#if RETAIL_COMPATIBLE_CRC || RETAIL_COMPATIBLE_XFER_SAVE + UnsignedInt m_lastDrivingClickFrame; // Frame number for retail compatibility + UnsignedInt m_2ndLastDrivingClickFrame; // Frame number for retail compatibility +#else + UnsignedInt m_lastDrivingClickTimeMsec; // Real-time milliseconds + UnsignedInt m_2ndLastDrivingClickTimeMsec; // Real-time milliseconds +#endif Bool m_upBonesCached; Bool m_defaultInfoCached; diff --git a/Generals/Code/GameEngine/Source/GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp b/Generals/Code/GameEngine/Source/GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp index ac6c94746f..747e188c5a 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp @@ -183,8 +183,13 @@ ParticleUplinkCannonUpdate::ParticleUplinkCannonUpdate( Thing *thing, const Modu m_nextDamagePulseFrame = 0; m_startAttackFrame = 0; m_startDecayFrame = 0; +#if RETAIL_COMPATIBLE_CRC || RETAIL_COMPATIBLE_XFER_SAVE m_lastDrivingClickFrame = 0; m_2ndLastDrivingClickFrame = 0; +#else + m_lastDrivingClickTimeMsec = 0; + m_2ndLastDrivingClickTimeMsec = 0; +#endif m_clientShroudedLastFrame = FALSE; for( Int i = 0; i < MAX_OUTER_NODES; i++ ) @@ -324,8 +329,13 @@ void ParticleUplinkCannonUpdate::setSpecialPowerOverridableDestination( const Co { m_overrideTargetDestination = *loc; m_manualTargetMode = true; +#if RETAIL_COMPATIBLE_CRC || RETAIL_COMPATIBLE_XFER_SAVE m_2ndLastDrivingClickFrame = m_lastDrivingClickFrame; m_lastDrivingClickFrame = TheGameLogic->getFrame(); +#else + m_2ndLastDrivingClickTimeMsec = m_lastDrivingClickTimeMsec; + m_lastDrivingClickTimeMsec = timeGetTime(); +#endif } } @@ -517,7 +527,11 @@ UpdateSleepTime ParticleUplinkCannonUpdate::update() else { Real speed = data->m_manualDrivingSpeed; - const Bool useFasterSpeed = m_lastDrivingClickFrame - m_2ndLastDrivingClickFrame < data->m_doubleClickToFastDriveDelay; +#if RETAIL_COMPATIBLE_CRC + const Bool useFasterSpeed = m_lastDrivingClickFrame != 0 && m_2ndLastDrivingClickFrame != 0 && m_lastDrivingClickFrame - m_2ndLastDrivingClickFrame < data->m_doubleClickToFastDriveDelay; +#else + const Bool useFasterSpeed = m_lastDrivingClickTimeMsec != 0 && m_2ndLastDrivingClickTimeMsec != 0 && m_lastDrivingClickTimeMsec - m_2ndLastDrivingClickTimeMsec < data->m_doubleClickToFastDriveDelay; +#endif if( useFasterSpeed ) { //Because we double clicked, use the faster driving speed. @@ -1309,7 +1323,12 @@ void ParticleUplinkCannonUpdate::crc( Xfer *xfer ) // ------------------------------------------------------------------------------------------------ /** Xfer method * Version Info: - * 1: Initial version */ + * 1: Initial version + * 2: Added m_startDecayFrame + * 3: Added m_manualTargetMode + * 4: Added m_orbitToTargetLaserRadius + * 5: Changed m_lastDrivingClickFrame and m_2ndLastDrivingClickFrame to m_lastDrivingClickTimeMsec and m_2ndLastDrivingClickTimeMsec (frames to milliseconds) + */ // ------------------------------------------------------------------------------------------------ void ParticleUplinkCannonUpdate::xfer( Xfer *xfer ) { @@ -1317,9 +1336,9 @@ void ParticleUplinkCannonUpdate::xfer( Xfer *xfer ) // version #if RETAIL_COMPATIBLE_XFER_SAVE - const XferVersion currentVersion = 2; + const XferVersion currentVersion = 3; #else - const XferVersion currentVersion = 4; + const XferVersion currentVersion = 5; #endif XferVersion version = currentVersion; xfer->xferVersion( &version, currentVersion ); @@ -1416,10 +1435,35 @@ void ParticleUplinkCannonUpdate::xfer( Xfer *xfer ) } // the time of last manual target click +#if RETAIL_COMPATIBLE_CRC || RETAIL_COMPATIBLE_XFER_SAVE + // Retail builds stay at version 3 for compatibility, so always use old frame format xfer->xferUnsignedInt( &m_lastDrivingClickFrame ); - - // the time of the 2nd last manual target click xfer->xferUnsignedInt( &m_2ndLastDrivingClickFrame ); +#else + if( version >= 5 ) + { + xfer->xferUnsignedInt( &m_lastDrivingClickTimeMsec ); + xfer->xferUnsignedInt( &m_2ndLastDrivingClickTimeMsec ); + } + else + { + // Old versions stored frame numbers, which we can't meaningfully convert to milliseconds + UnsignedInt oldLastDrivingClickFrame = 0; + UnsignedInt old2ndLastDrivingClickFrame = 0; + xfer->xferUnsignedInt( &oldLastDrivingClickFrame ); + xfer->xferUnsignedInt( &old2ndLastDrivingClickFrame ); + if( xfer->getXferMode() == XFER_LOAD ) + { + m_lastDrivingClickTimeMsec = 0; + m_2ndLastDrivingClickTimeMsec = 0; + } + } +#endif + + if( version >= 3 ) + { + xfer->xferBool( &m_manualTargetMode ); + } if( version >= 4 ) { From c01dbb23148009549937062550fc789a4f6ee37e Mon Sep 17 00:00:00 2001 From: Bobby Battista Date: Sun, 21 Dec 2025 18:53:48 -0500 Subject: [PATCH 7/7] refactor(input): Remove unnecessary null checks from double-click timing, add debug assertions --- .../GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp | 6 ++++-- .../GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Generals/Code/GameEngine/Source/GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp b/Generals/Code/GameEngine/Source/GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp index 747e188c5a..7c8141b3b1 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp @@ -528,9 +528,11 @@ UpdateSleepTime ParticleUplinkCannonUpdate::update() { Real speed = data->m_manualDrivingSpeed; #if RETAIL_COMPATIBLE_CRC - const Bool useFasterSpeed = m_lastDrivingClickFrame != 0 && m_2ndLastDrivingClickFrame != 0 && m_lastDrivingClickFrame - m_2ndLastDrivingClickFrame < data->m_doubleClickToFastDriveDelay; + DEBUG_ASSERTCRASH(m_lastDrivingClickFrame >= m_2ndLastDrivingClickFrame, ("m_lastDrivingClickFrame should always be >= m_2ndLastDrivingClickFrame")); + const Bool useFasterSpeed = m_lastDrivingClickFrame - m_2ndLastDrivingClickFrame < data->m_doubleClickToFastDriveDelay; #else - const Bool useFasterSpeed = m_lastDrivingClickTimeMsec != 0 && m_2ndLastDrivingClickTimeMsec != 0 && m_lastDrivingClickTimeMsec - m_2ndLastDrivingClickTimeMsec < data->m_doubleClickToFastDriveDelay; + DEBUG_ASSERTCRASH(m_lastDrivingClickTimeMsec >= m_2ndLastDrivingClickTimeMsec, ("m_lastDrivingClickTimeMsec should always be >= m_2ndLastDrivingClickTimeMsec")); + const Bool useFasterSpeed = m_lastDrivingClickTimeMsec - m_2ndLastDrivingClickTimeMsec < data->m_doubleClickToFastDriveDelay; #endif if( useFasterSpeed ) { diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp index 925f047b6f..7ee4251c81 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp @@ -578,9 +578,11 @@ UpdateSleepTime ParticleUplinkCannonUpdate::update() { Real speed = data->m_manualDrivingSpeed; #if RETAIL_COMPATIBLE_CRC - const Bool useFasterSpeed = m_scriptedWaypointMode || (m_lastDrivingClickFrame != 0 && m_2ndLastDrivingClickFrame != 0 && m_lastDrivingClickFrame - m_2ndLastDrivingClickFrame < data->m_doubleClickToFastDriveDelay); + DEBUG_ASSERTCRASH(m_lastDrivingClickFrame >= m_2ndLastDrivingClickFrame, ("m_lastDrivingClickFrame should always be >= m_2ndLastDrivingClickFrame")); + const Bool useFasterSpeed = m_scriptedWaypointMode || (m_lastDrivingClickFrame - m_2ndLastDrivingClickFrame < data->m_doubleClickToFastDriveDelay); #else - const Bool useFasterSpeed = m_scriptedWaypointMode || (m_lastDrivingClickTimeMsec != 0 && m_2ndLastDrivingClickTimeMsec != 0 && m_lastDrivingClickTimeMsec - m_2ndLastDrivingClickTimeMsec < data->m_doubleClickToFastDriveDelay); + DEBUG_ASSERTCRASH(m_lastDrivingClickTimeMsec >= m_2ndLastDrivingClickTimeMsec, ("m_lastDrivingClickTimeMsec should always be >= m_2ndLastDrivingClickTimeMsec")); + const Bool useFasterSpeed = m_scriptedWaypointMode || (m_lastDrivingClickTimeMsec - m_2ndLastDrivingClickTimeMsec < data->m_doubleClickToFastDriveDelay); #endif if( useFasterSpeed ) {