Skip to content

Commit 31559b2

Browse files
committed
feat(particle-uplink): add client-side double-click detection for fast beam mode behind RETAIL_COMPATIBLE_CRC
1 parent c9b48ce commit 31559b2

File tree

9 files changed

+59
-31
lines changed

9 files changed

+59
-31
lines changed

Generals/Code/GameEngine/Source/GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333
#define DEFINE_DEATH_NAMES
3434

3535
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
36-
#include "Common/FramePacer.h"
3736
#include "Common/GameUtility.h"
3837
#include "Common/ThingTemplate.h"
3938
#include "Common/ThingFactory.h"
@@ -519,16 +518,7 @@ UpdateSleepTime ParticleUplinkCannonUpdate::update()
519518
else
520519
{
521520
Real speed = data->m_manualDrivingSpeed;
522-
DEBUG_ASSERTCRASH(m_lastDrivingClickFrame >= m_2ndLastDrivingClickFrame, ("m_lastDrivingClickFrame should always be >= m_2ndLastDrivingClickFrame"));
523-
// TheSuperHackers @fix Scale delay threshold by frame rate and logic time scale to maintain consistent real-time feel
524-
UnsignedInt scaledDelay = data->m_doubleClickToFastDriveDelay * LOGICFRAMES_PER_SECOND / BaseFps;
525-
if (TheFramePacer != NULL)
526-
{
527-
const Real timeScaleRatio = TheFramePacer->getActualLogicTimeScaleRatio();
528-
scaledDelay = (UnsignedInt)(scaledDelay * timeScaleRatio);
529-
}
530-
const Bool useFasterSpeed = m_lastDrivingClickFrame - m_2ndLastDrivingClickFrame < scaledDelay;
531-
if( useFasterSpeed )
521+
if( m_lastDrivingClickFrame - m_2ndLastDrivingClickFrame < data->m_doubleClickToFastDriveDelay )
532522
{
533523
//Because we double clicked, use the faster driving speed.
534524
speed = data->m_manualFastDrivingSpeed;

GeneralsMD/Code/GameEngine/Include/GameClient/CommandXlat.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ class CommandTranslator : public GameMessageTranslator
5656
UnsignedInt m_mouseRightDown; // when the mouse down happened
5757
UnsignedInt m_mouseRightUp; // when the mouse up happened
5858

59+
#if !RETAIL_COMPATIBLE_CRC
60+
// TheSuperHackers @feature bobtista 01/08/2026 Double-click detection for fast beam mode
61+
UnsignedInt m_lastSpecialPowerOverrideClickTime; // real time of last special power override destination click
62+
#endif
63+
5964
GameMessage::Type createMoveToLocationMessage( Drawable *draw, const Coord3D *dest, CommandEvaluateType commandType );
6065
GameMessage::Type createAttackMessage( Drawable *draw, Drawable *other, CommandEvaluateType commandType );
6166
GameMessage::Type createEnterMessage( Drawable *enter, CommandEvaluateType commandType );

GeneralsMD/Code/GameEngine/Include/GameLogic/AI.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -959,7 +959,7 @@ class AIGroup : public MemoryPoolObject, public Snapshot
959959
void groupDoCommandButtonUsingWaypoints( const CommandButton *commandButton, const Waypoint *way, CommandSourceType cmdSource );
960960
void groupDoCommandButtonAtObject( const CommandButton *commandButton, Object *obj, CommandSourceType cmdSource );
961961
void groupSetEmoticon( const AsciiString &name, Int duration );
962-
void groupOverrideSpecialPowerDestination( SpecialPowerType spType, const Coord3D *loc, CommandSourceType cmdSource );
962+
void groupOverrideSpecialPowerDestination( SpecialPowerType spType, const Coord3D *loc, CommandSourceType cmdSource, Bool useFastDrive = FALSE ); ///< TheSuperHackers @feature bobtista 01/08/2026 Added useFastDrive parameter
963963

964964
void setAttitude( AttitudeType tude ); ///< set the behavior modifier for this agent
965965
AttitudeType getAttitude( void ) const; ///< get the current behavior modifier state

GeneralsMD/Code/GameEngine/Include/GameLogic/Module/ParticleUplinkCannonUpdate.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ class ParticleUplinkCannonUpdate : public SpecialPowerUpdateModule
182182

183183
virtual Bool doesSpecialPowerHaveOverridableDestinationActive() const; //Is it active now?
184184
virtual Bool doesSpecialPowerHaveOverridableDestination() const { return true; } //Does it have it, even if it's not active?
185-
virtual void setSpecialPowerOverridableDestination( const Coord3D *loc );
185+
virtual void setSpecialPowerOverridableDestination( const Coord3D *loc, Bool useFastDrive = FALSE ); ///< TheSuperHackers @feature bobtista 01/08/2026 Added useFastDrive parameter
186186

187187
// Disabled conditions to process (termination conditions!)
188188
virtual DisabledMaskType getDisabledTypesToProcess() const { return MAKE_DISABLED_MASK4( DISABLED_SUBDUED, DISABLED_UNDERPOWERED, DISABLED_EMP, DISABLED_HACKED ); }
@@ -239,4 +239,7 @@ class ParticleUplinkCannonUpdate : public SpecialPowerUpdateModule
239239
Bool m_manualTargetMode;
240240
Bool m_scriptedWaypointMode;
241241
Bool m_clientShroudedLastFrame;
242+
#if !RETAIL_COMPATIBLE_CRC
243+
Bool m_useFastDrive; ///< TheSuperHackers @feature bobtista 01/08/2026 Fast beam mode from explicit message
244+
#endif
242245
};

GeneralsMD/Code/GameEngine/Include/GameLogic/Module/SpecialPowerUpdateModule.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ class SpecialPowerUpdateInterface
4646
virtual CommandOption getCommandOption() const = 0;
4747
virtual Bool doesSpecialPowerHaveOverridableDestinationActive() const = 0; //Is it active now?
4848
virtual Bool doesSpecialPowerHaveOverridableDestination() const = 0; //Does it have it, even if it's not active?
49-
virtual void setSpecialPowerOverridableDestination( const Coord3D *loc ) = 0;
49+
virtual void setSpecialPowerOverridableDestination( const Coord3D *loc, Bool useFastDrive = FALSE ) = 0; ///< TheSuperHackers @feature bobtista 01/08/2026 Added useFastDrive parameter
5050
virtual Bool isPowerCurrentlyInUse( const CommandButton *command = NULL ) const = 0;
5151
};
5252

@@ -74,7 +74,7 @@ class SpecialPowerUpdateModule : public UpdateModule, public SpecialPowerUpdateI
7474
virtual CommandOption getCommandOption() const = 0;
7575
virtual Bool doesSpecialPowerHaveOverridableDestinationActive() const = 0; //Is it active now?
7676
virtual Bool doesSpecialPowerHaveOverridableDestination() const = 0; //Does it have it, even if it's not active?
77-
virtual void setSpecialPowerOverridableDestination( const Coord3D *loc ) = 0;
77+
virtual void setSpecialPowerOverridableDestination( const Coord3D *loc, Bool useFastDrive = FALSE ) = 0; ///< TheSuperHackers @feature bobtista 01/08/2026 Added useFastDrive parameter
7878
virtual Bool isPowerCurrentlyInUse( const CommandButton *command = NULL ) const = 0;
7979

8080
};

GeneralsMD/Code/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1462,6 +1462,9 @@ CommandTranslator::CommandTranslator() :
14621462
m_teamExists(false),
14631463
m_mouseRightDown(0),
14641464
m_mouseRightUp(0)
1465+
#if !RETAIL_COMPATIBLE_CRC
1466+
, m_lastSpecialPowerOverrideClickTime(0)
1467+
#endif
14651468
{
14661469
m_mouseRightDragAnchor.x = 0;
14671470
m_mouseRightDragAnchor.y = 0;
@@ -1836,11 +1839,23 @@ GameMessage::Type CommandTranslator::evaluateContextCommand( Drawable *draw,
18361839
msgType = GameMessage::MSG_DO_SPECIAL_POWER_OVERRIDE_DESTINATION;
18371840
if( type == DO_COMMAND )
18381841
{
1842+
#if !RETAIL_COMPATIBLE_CRC
1843+
// TheSuperHackers @feature bobtista 01/08/2026 Double-click detection for fast beam mode.
1844+
// Detect double-clicks using real time instead of frame-based logic for consistent timing.
1845+
const UnsignedInt DOUBLE_CLICK_DELAY_MS = 500;
1846+
UnsignedInt nowMs = timeGetTime();
1847+
Bool isDoubleClick = (nowMs - m_lastSpecialPowerOverrideClickTime) < DOUBLE_CLICK_DELAY_MS;
1848+
m_lastSpecialPowerOverrideClickTime = nowMs;
1849+
#endif
1850+
18391851
GameMessage *gameMsg = TheMessageStream->appendMessage( msgType );
18401852

18411853
gameMsg->appendLocationArgument( *pos );
18421854
gameMsg->appendIntegerArgument( SPECIAL_INVALID );
18431855
gameMsg->appendObjectIDArgument( INVALID_ID ); // no specific source
1856+
#if !RETAIL_COMPATIBLE_CRC
1857+
gameMsg->appendBooleanArgument( isDoubleClick ); // TheSuperHackers @feature bobtista 01/08/2026 Fast beam mode flag
1858+
#endif
18441859

18451860
}
18461861

GeneralsMD/Code/GameEngine/Source/GameLogic/AI/AIGroup.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3297,7 +3297,9 @@ void AIGroup::groupSetEmoticon( const AsciiString &name, Int duration )
32973297
}
32983298

32993299
//-----------------------------------------------------------------------------
3300-
void AIGroup::groupOverrideSpecialPowerDestination( SpecialPowerType spType, const Coord3D *loc, CommandSourceType cmdSource )
3300+
// TheSuperHackers @feature bobtista 01/08/2026 Added useFastDrive parameter
3301+
//-----------------------------------------------------------------------------
3302+
void AIGroup::groupOverrideSpecialPowerDestination( SpecialPowerType spType, const Coord3D *loc, CommandSourceType cmdSource, Bool useFastDrive )
33013303
{
33023304
std::list<Object *>::iterator i;
33033305
for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
@@ -3308,7 +3310,7 @@ void AIGroup::groupOverrideSpecialPowerDestination( SpecialPowerType spType, con
33083310
SpecialPowerUpdateInterface *spuInterface = object->findSpecialPowerWithOverridableDestinationActive( spType );
33093311
if( spuInterface )
33103312
{
3311-
spuInterface->setSpecialPowerOverridableDestination( loc );
3313+
spuInterface->setSpecialPowerOverridableDestination( loc, useFastDrive );
33123314
}
33133315
}
33143316
}

GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
#define DEFINE_DEATH_NAMES
3333

3434
// INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
35-
#include "Common/FramePacer.h"
3635
#include "Common/GameUtility.h"
3736
#include "Common/ThingTemplate.h"
3837
#include "Common/ThingFactory.h"
@@ -189,6 +188,9 @@ ParticleUplinkCannonUpdate::ParticleUplinkCannonUpdate( Thing *thing, const Modu
189188
m_lastDrivingClickFrame = 0;
190189
m_2ndLastDrivingClickFrame = 0;
191190
m_clientShroudedLastFrame = FALSE;
191+
#if !RETAIL_COMPATIBLE_CRC
192+
m_useFastDrive = FALSE;
193+
#endif
192194

193195
for( Int i = 0; i < MAX_OUTER_NODES; i++ )
194196
{
@@ -370,14 +372,19 @@ Bool ParticleUplinkCannonUpdate::isPowerCurrentlyInUse( const CommandButton *com
370372
}
371373

372374
//-------------------------------------------------------------------------------------------------
373-
void ParticleUplinkCannonUpdate::setSpecialPowerOverridableDestination( const Coord3D *loc )
375+
// TheSuperHackers @feature bobtista 01/08/2026 Added useFastDrive parameter
376+
//-------------------------------------------------------------------------------------------------
377+
void ParticleUplinkCannonUpdate::setSpecialPowerOverridableDestination( const Coord3D *loc, Bool useFastDrive )
374378
{
375379
if( !getObject()->isDisabled() )
376380
{
377381
m_overrideTargetDestination = *loc;
378382
m_manualTargetMode = TRUE;
379383
m_2ndLastDrivingClickFrame = m_lastDrivingClickFrame;
380384
m_lastDrivingClickFrame = TheGameLogic->getFrame();
385+
#if !RETAIL_COMPATIBLE_CRC
386+
m_useFastDrive = useFastDrive;
387+
#endif
381388
}
382389
}
383390

@@ -569,16 +576,14 @@ UpdateSleepTime ParticleUplinkCannonUpdate::update()
569576
else
570577
{
571578
Real speed = data->m_manualDrivingSpeed;
572-
DEBUG_ASSERTCRASH(m_lastDrivingClickFrame >= m_2ndLastDrivingClickFrame, ("m_lastDrivingClickFrame should always be >= m_2ndLastDrivingClickFrame"));
573-
// TheSuperHackers @fix Scale delay threshold by frame rate and logic time scale to maintain consistent real-time feel
574-
UnsignedInt scaledDelay = data->m_doubleClickToFastDriveDelay * LOGICFRAMES_PER_SECOND / BaseFps;
575-
if (TheFramePacer != NULL)
576-
{
577-
const Real timeScaleRatio = TheFramePacer->getActualLogicTimeScaleRatio();
578-
scaledDelay = (UnsignedInt)(scaledDelay * timeScaleRatio);
579-
}
580-
const Bool useFasterSpeed = m_scriptedWaypointMode || (m_lastDrivingClickFrame - m_2ndLastDrivingClickFrame < scaledDelay);
581-
if( useFasterSpeed )
579+
580+
// TheSuperHackers @feature bobtista 01/08/2026 Use explicit fast drive flag in non-retail builds.
581+
// In retail builds, use the original frame-based double-click detection.
582+
#if !RETAIL_COMPATIBLE_CRC
583+
if( m_scriptedWaypointMode || m_useFastDrive )
584+
#else
585+
if( m_scriptedWaypointMode || m_lastDrivingClickFrame - m_2ndLastDrivingClickFrame < data->m_doubleClickToFastDriveDelay )
586+
#endif
582587
{
583588
//Because we double clicked, use the faster driving speed.
584589
speed = data->m_manualFastDrivingSpeed;

GeneralsMD/Code/GameEngine/Source/GameLogic/System/GameLogicDispatch.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1209,11 +1209,19 @@ void GameLogic::logicMessageDispatcher( GameMessage *msg, void *userData )
12091209

12101210
ObjectID sourceID = msg->getArgument(2)->objectID;
12111211
Object* source = findObjectByID(sourceID);
1212+
1213+
#if !RETAIL_COMPATIBLE_CRC
1214+
// TheSuperHackers @feature bobtista 01/08/2026 Fast beam mode from bool argument
1215+
Bool useFastDrive = msg->getArgumentCount() > 3 ? msg->getArgument(3)->boolean : FALSE;
1216+
#else
1217+
Bool useFastDrive = FALSE;
1218+
#endif
1219+
12121220
if (source != NULL)
12131221
{
12141222
AIGroupPtr theGroup = TheAI->createGroup();
12151223
theGroup->add(source);
1216-
theGroup->groupOverrideSpecialPowerDestination( spType, loc, CMD_FROM_PLAYER );
1224+
theGroup->groupOverrideSpecialPowerDestination( spType, loc, CMD_FROM_PLAYER, useFastDrive );
12171225
#if RETAIL_COMPATIBLE_AIGROUP
12181226
TheAI->destroyGroup(theGroup);
12191227
#else
@@ -1224,7 +1232,7 @@ void GameLogic::logicMessageDispatcher( GameMessage *msg, void *userData )
12241232
{
12251233
if( currentlySelectedGroup )
12261234
{
1227-
currentlySelectedGroup->groupOverrideSpecialPowerDestination( spType, loc, CMD_FROM_PLAYER );
1235+
currentlySelectedGroup->groupOverrideSpecialPowerDestination( spType, loc, CMD_FROM_PLAYER, useFastDrive );
12281236
}
12291237
}
12301238

0 commit comments

Comments
 (0)