Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
fe65f8a
fix(input): Convert double-click fast drive timing from frames to rea…
bobtista Dec 19, 2025
757a4d9
refactor(input): Extract double-click condition to variable in Partic…
bobtista Dec 20, 2025
a40f288
style: Fix spacing inconsistency in ParticleUplinkCannon xfer function
bobtista Dec 20, 2025
750cf34
refactor: Make millisecond variables conditional in ParticleUplinkCan…
bobtista Dec 20, 2025
69992b6
fix(input): Fix double-click fast drive timing in ParticleUplinkCannon
bobtista Dec 21, 2025
51afdcc
Replicate to generals
bobtista Dec 21, 2025
4c8d39f
refactor(input): Remove unnecessary null checks from double-click tim…
bobtista Dec 21, 2025
55b8a0e
Use RETAIL_COMPATIBLE_CRC || RETAIL_COMPATIBLE_XFER_SAVE for Particle…
bobtista Dec 31, 2025
24fd78e
fix(particle-uplink): use consistent RETAIL_COMPATIBLE macros and fix…
bobtista Jan 1, 2026
35efd03
refactor(particle-uplink): parse double-click delay as milliseconds f…
bobtista Jan 1, 2026
2723f6e
nit: update comments to match our format
bobtista Jan 2, 2026
d80e949
remove redundant xfer load branch in ParticleUplinkCannonUpdate
bobtista Jan 3, 2026
59b0786
remove xfer of timeGetTime values and fix xfer version numbering
bobtista Jan 6, 2026
d74ec2c
refactor(particle-uplink): use frame scaling for double-click delay i…
bobtista Jan 7, 2026
52f5452
fix(particle-uplink): scale double-click delay by logic time scale fo…
bobtista Jan 7, 2026
c9b48ce
revert xfer changes that were out of scope for this PR
bobtista Jan 7, 2026
6ea27b9
feat(particle-uplink): add client-side double-click detection for fas…
bobtista Jan 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions GeneralsMD/Code/GameEngine/Include/GameClient/CommandXlat.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ class CommandTranslator : public GameMessageTranslator
UnsignedInt m_mouseRightDown; // when the mouse down happened
UnsignedInt m_mouseRightUp; // when the mouse up happened

#if !RETAIL_COMPATIBLE_CRC
// TheSuperHackers @feature bobtista 01/08/2026 Double-click detection for fast beam mode
UnsignedInt m_lastSpecialPowerOverrideClickTime; // real time of last special power override destination click
#endif

GameMessage::Type createMoveToLocationMessage( Drawable *draw, const Coord3D *dest, CommandEvaluateType commandType );
GameMessage::Type createAttackMessage( Drawable *draw, Drawable *other, CommandEvaluateType commandType );
GameMessage::Type createEnterMessage( Drawable *enter, CommandEvaluateType commandType );
Expand Down
2 changes: 1 addition & 1 deletion GeneralsMD/Code/GameEngine/Include/GameLogic/AI.h
Original file line number Diff line number Diff line change
Expand Up @@ -959,7 +959,7 @@ class AIGroup : public MemoryPoolObject, public Snapshot
void groupDoCommandButtonUsingWaypoints( const CommandButton *commandButton, const Waypoint *way, CommandSourceType cmdSource );
void groupDoCommandButtonAtObject( const CommandButton *commandButton, Object *obj, CommandSourceType cmdSource );
void groupSetEmoticon( const AsciiString &name, Int duration );
void groupOverrideSpecialPowerDestination( SpecialPowerType spType, const Coord3D *loc, CommandSourceType cmdSource );
void groupOverrideSpecialPowerDestination( SpecialPowerType spType, const Coord3D *loc, CommandSourceType cmdSource, Bool useFastDrive = FALSE ); ///< TheSuperHackers @feature bobtista 01/08/2026 Added useFastDrive parameter

void setAttitude( AttitudeType tude ); ///< set the behavior modifier for this agent
AttitudeType getAttitude( void ) const; ///< get the current behavior modifier state
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ class BattlePlanUpdate : public SpecialPowerUpdateModule
virtual SpecialPowerUpdateInterface* getSpecialPowerUpdateInterface() { return this; }
virtual Bool doesSpecialPowerHaveOverridableDestinationActive() const { return false; } //Is it active now?
virtual Bool doesSpecialPowerHaveOverridableDestination() const { return false; } //Does it have it, even if it's not active?
virtual void setSpecialPowerOverridableDestination( const Coord3D *loc ) {}
virtual void setSpecialPowerOverridableDestination( const Coord3D *loc, Bool useFastDrive = FALSE ) {}
virtual Bool isPowerCurrentlyInUse( const CommandButton *command = NULL ) const;

//Returns the currently active battle plan -- unpacked and ready... returns PLANSTATUS_NONE if in transition!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ class MissileLauncherBuildingUpdate : public SpecialPowerUpdateModule
SpecialPowerTemplate* getTemplate() const;
virtual Bool doesSpecialPowerHaveOverridableDestinationActive() const { return false; } //Is it active now?
virtual Bool doesSpecialPowerHaveOverridableDestination() const { return false; } //Does it have it, even if it's not active?
virtual void setSpecialPowerOverridableDestination( const Coord3D *loc ) {}
virtual void setSpecialPowerOverridableDestination( const Coord3D *loc, Bool useFastDrive = FALSE ) {}

virtual SpecialPowerUpdateInterface* getSpecialPowerUpdateInterface() { return this; }
virtual CommandOption getCommandOption() const { return (CommandOption)0; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ class ParticleUplinkCannonUpdate : public SpecialPowerUpdateModule

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

// Disabled conditions to process (termination conditions!)
virtual DisabledMaskType getDisabledTypesToProcess() const { return MAKE_DISABLED_MASK4( DISABLED_SUBDUED, DISABLED_UNDERPOWERED, DISABLED_EMP, DISABLED_HACKED ); }
Expand Down Expand Up @@ -239,4 +239,7 @@ class ParticleUplinkCannonUpdate : public SpecialPowerUpdateModule
Bool m_manualTargetMode;
Bool m_scriptedWaypointMode;
Bool m_clientShroudedLastFrame;
#if !RETAIL_COMPATIBLE_CRC
Bool m_useFastDrive; ///< TheSuperHackers @feature bobtista 01/08/2026 Fast beam mode from explicit message
#endif
};
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ class SpecialAbilityUpdate : public SpecialPowerUpdateModule
virtual Bool isActive() const { return m_active; }
virtual Bool doesSpecialPowerHaveOverridableDestinationActive() const { return false; } //Is it active now?
virtual Bool doesSpecialPowerHaveOverridableDestination() const { return false; } //Does it have it, even if it's not active?
virtual void setSpecialPowerOverridableDestination( const Coord3D *loc ) {}
virtual void setSpecialPowerOverridableDestination( const Coord3D *loc, Bool useFastDrive = FALSE ) {}
virtual Bool isPowerCurrentlyInUse( const CommandButton *command = NULL ) const;

// virtual Bool isBusy() const { return m_isBusy; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class SpecialPowerUpdateInterface
virtual CommandOption getCommandOption() const = 0;
virtual Bool doesSpecialPowerHaveOverridableDestinationActive() const = 0; //Is it active now?
virtual Bool doesSpecialPowerHaveOverridableDestination() const = 0; //Does it have it, even if it's not active?
virtual void setSpecialPowerOverridableDestination( const Coord3D *loc ) = 0;
virtual void setSpecialPowerOverridableDestination( const Coord3D *loc, Bool useFastDrive = FALSE ) = 0; ///< TheSuperHackers @feature bobtista 01/08/2026 Added useFastDrive parameter
virtual Bool isPowerCurrentlyInUse( const CommandButton *command = NULL ) const = 0;
};

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

};
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ class SpectreGunshipDeploymentUpdate : public SpecialPowerUpdateModule

virtual Bool doesSpecialPowerHaveOverridableDestinationActive() const { return FALSE; };
virtual Bool doesSpecialPowerHaveOverridableDestination() const { return FALSE; } //Does it have it, even if it's not active?
virtual void setSpecialPowerOverridableDestination( const Coord3D *loc ) {};
virtual void setSpecialPowerOverridableDestination( const Coord3D *loc, Bool useFastDrive = FALSE ) {};

// Disabled conditions to process (termination conditions!)
virtual DisabledMaskType getDisabledTypesToProcess() const { return MAKE_DISABLED_MASK4( DISABLED_SUBDUED, DISABLED_UNDERPOWERED, DISABLED_EMP, DISABLED_HACKED ); }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ class SpectreGunshipUpdate : public SpecialPowerUpdateModule

virtual Bool doesSpecialPowerHaveOverridableDestinationActive() const;
virtual Bool doesSpecialPowerHaveOverridableDestination() const { return true; } //Does it have it, even if it's not active?
virtual void setSpecialPowerOverridableDestination( const Coord3D *loc );
virtual void setSpecialPowerOverridableDestination( const Coord3D *loc, Bool useFastDrive = FALSE );

// Disabled conditions to process (termination conditions!)
virtual DisabledMaskType getDisabledTypesToProcess() const { return MAKE_DISABLED_MASK4( DISABLED_SUBDUED, DISABLED_UNDERPOWERED, DISABLED_EMP, DISABLED_HACKED ); }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1462,6 +1462,9 @@ CommandTranslator::CommandTranslator() :
m_teamExists(false),
m_mouseRightDown(0),
m_mouseRightUp(0)
#if !RETAIL_COMPATIBLE_CRC
, m_lastSpecialPowerOverrideClickTime(0)
#endif
{
m_mouseRightDragAnchor.x = 0;
m_mouseRightDragAnchor.y = 0;
Expand Down Expand Up @@ -1836,11 +1839,23 @@ GameMessage::Type CommandTranslator::evaluateContextCommand( Drawable *draw,
msgType = GameMessage::MSG_DO_SPECIAL_POWER_OVERRIDE_DESTINATION;
if( type == DO_COMMAND )
{
#if !RETAIL_COMPATIBLE_CRC
// TheSuperHackers @feature bobtista 01/08/2026 Double-click detection for fast beam mode.
// Detect double-clicks using real time instead of frame-based logic for consistent timing.
const UnsignedInt DOUBLE_CLICK_DELAY_MS = 500;
UnsignedInt nowMs = timeGetTime();
Bool isDoubleClick = (nowMs - m_lastSpecialPowerOverrideClickTime) < DOUBLE_CLICK_DELAY_MS;
m_lastSpecialPowerOverrideClickTime = nowMs;
#endif

GameMessage *gameMsg = TheMessageStream->appendMessage( msgType );

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

}

Expand Down
6 changes: 4 additions & 2 deletions GeneralsMD/Code/GameEngine/Source/GameLogic/AI/AIGroup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3297,7 +3297,9 @@ void AIGroup::groupSetEmoticon( const AsciiString &name, Int duration )
}

//-----------------------------------------------------------------------------
void AIGroup::groupOverrideSpecialPowerDestination( SpecialPowerType spType, const Coord3D *loc, CommandSourceType cmdSource )
// TheSuperHackers @feature bobtista 01/08/2026 Added useFastDrive parameter
//-----------------------------------------------------------------------------
void AIGroup::groupOverrideSpecialPowerDestination( SpecialPowerType spType, const Coord3D *loc, CommandSourceType cmdSource, Bool useFastDrive )
{
std::list<Object *>::iterator i;
for( i = m_memberList.begin(); i != m_memberList.end(); ++i )
Expand All @@ -3308,7 +3310,7 @@ void AIGroup::groupOverrideSpecialPowerDestination( SpecialPowerType spType, con
SpecialPowerUpdateInterface *spuInterface = object->findSpecialPowerWithOverridableDestinationActive( spType );
if( spuInterface )
{
spuInterface->setSpecialPowerOverridableDestination( loc );
spuInterface->setSpecialPowerOverridableDestination( loc, useFastDrive );
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,9 @@ ParticleUplinkCannonUpdate::ParticleUplinkCannonUpdate( Thing *thing, const Modu
m_lastDrivingClickFrame = 0;
m_2ndLastDrivingClickFrame = 0;
m_clientShroudedLastFrame = FALSE;
#if !RETAIL_COMPATIBLE_CRC
m_useFastDrive = FALSE;
#endif

for( Int i = 0; i < MAX_OUTER_NODES; i++ )
{
Expand Down Expand Up @@ -369,14 +372,19 @@ Bool ParticleUplinkCannonUpdate::isPowerCurrentlyInUse( const CommandButton *com
}

//-------------------------------------------------------------------------------------------------
void ParticleUplinkCannonUpdate::setSpecialPowerOverridableDestination( const Coord3D *loc )
// TheSuperHackers @feature bobtista 01/08/2026 Added useFastDrive parameter
//-------------------------------------------------------------------------------------------------
void ParticleUplinkCannonUpdate::setSpecialPowerOverridableDestination( const Coord3D *loc, Bool useFastDrive )
{
if( !getObject()->isDisabled() )
{
m_overrideTargetDestination = *loc;
m_manualTargetMode = TRUE;
m_2ndLastDrivingClickFrame = m_lastDrivingClickFrame;
m_lastDrivingClickFrame = TheGameLogic->getFrame();
#if !RETAIL_COMPATIBLE_CRC
m_useFastDrive = useFastDrive;
#endif
}
}

Expand Down Expand Up @@ -568,7 +576,14 @@ UpdateSleepTime ParticleUplinkCannonUpdate::update()
else
{
Real speed = data->m_manualDrivingSpeed;

// TheSuperHackers @feature bobtista 01/08/2026 Use explicit fast drive flag in non-retail builds.
// In retail builds, use the original frame-based double-click detection.
#if !RETAIL_COMPATIBLE_CRC
if( m_scriptedWaypointMode || m_useFastDrive )
#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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ Bool SpectreGunshipUpdate::isPowerCurrentlyInUse( const CommandButton *command )
}

//-------------------------------------------------------------------------------------------------
void SpectreGunshipUpdate::setSpecialPowerOverridableDestination( const Coord3D *loc )
void SpectreGunshipUpdate::setSpecialPowerOverridableDestination( const Coord3D *loc, Bool useFastDrive )
{
Object *me = getObject();
if( !me->isDisabled() )
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1209,11 +1209,19 @@ void GameLogic::logicMessageDispatcher( GameMessage *msg, void *userData )

ObjectID sourceID = msg->getArgument(2)->objectID;
Object* source = findObjectByID(sourceID);

#if !RETAIL_COMPATIBLE_CRC
// TheSuperHackers @feature bobtista 01/08/2026 Fast beam mode from bool argument
Bool useFastDrive = msg->getArgumentCount() > 3 ? msg->getArgument(3)->boolean : FALSE;
#else
Bool useFastDrive = FALSE;
#endif

if (source != NULL)
{
AIGroupPtr theGroup = TheAI->createGroup();
theGroup->add(source);
theGroup->groupOverrideSpecialPowerDestination( spType, loc, CMD_FROM_PLAYER );
theGroup->groupOverrideSpecialPowerDestination( spType, loc, CMD_FROM_PLAYER, useFastDrive );
#if RETAIL_COMPATIBLE_AIGROUP
TheAI->destroyGroup(theGroup);
#else
Expand All @@ -1224,7 +1232,7 @@ void GameLogic::logicMessageDispatcher( GameMessage *msg, void *userData )
{
if( currentlySelectedGroup )
{
currentlySelectedGroup->groupOverrideSpecialPowerDestination( spType, loc, CMD_FROM_PLAYER );
currentlySelectedGroup->groupOverrideSpecialPowerDestination( spType, loc, CMD_FROM_PLAYER, useFastDrive );
}
}

Expand Down
Loading