Skip to content

Commit 43af703

Browse files
committed
bugfix(production): Prevent cancelling production when units already produced
Signed-off-by: tintinhamans <5984296+tintinhamans@users.noreply.github.com>
1 parent d195e3b commit 43af703

4 files changed

Lines changed: 27 additions & 11 deletions

File tree

Generals/Code/GameEngine/Include/GameLogic/Module/ProductionUpdate.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ class ProductionUpdateInterface
158158
virtual UnsignedInt countUnitTypeInQueue( const ThingTemplate *unitType ) const = 0;
159159

160160
virtual Bool queueCreateUnit( const ThingTemplate *unitType, ProductionID productionID ) = 0;
161-
virtual void cancelUnitCreate( ProductionID productionID ) = 0;
161+
virtual void cancelUnitCreate( ProductionID productionID, Bool forceCancel = FALSE ) = 0;
162162
virtual void cancelAllUnitsOfType( const ThingTemplate *unitType) = 0;
163163

164164
virtual void cancelAndRefundAllProduction() = 0;
@@ -208,8 +208,8 @@ class ProductionUpdate : public UpdateModule, public ProductionUpdateInterface,
208208
virtual Bool isUpgradeInQueue( const UpgradeTemplate *upgrade ) const; ///< is the upgrade in our production queue already
209209
virtual UnsignedInt countUnitTypeInQueue( const ThingTemplate *unitType ) const; ///< count number of units with matching unit type in the production queue
210210

211-
virtual Bool queueCreateUnit( const ThingTemplate *unitType, ProductionID productionID ); ///< queue unit to be produced
212-
virtual void cancelUnitCreate( ProductionID productionID ); ///< cancel construction of unit with matching production ID
211+
virtual Bool queueCreateUnit( const ThingTemplate *unitType, ProductionID productionID ); ///< queue unit to be produced
212+
virtual void cancelUnitCreate( ProductionID productionID, Bool forceCancel = FALSE ); ///< cancel construction of unit with matching production ID
213213
virtual void cancelAllUnitsOfType( const ThingTemplate *unitType); ///< cancel all production of type unitType
214214

215215
virtual void cancelAndRefundAllProduction(); ///< cancel and refund anything in the production queue

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

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -457,7 +457,7 @@ Bool ProductionUpdate::queueCreateUnit( const ThingTemplate *unitType, Productio
457457
//-------------------------------------------------------------------------------------------------
458458
/** Cancel the construction of the unit with the matching production ID */
459459
//-------------------------------------------------------------------------------------------------
460-
void ProductionUpdate::cancelUnitCreate( ProductionID productionID )
460+
void ProductionUpdate::cancelUnitCreate( ProductionID productionID, Bool forceCancel )
461461
{
462462

463463
// search for the production entry in our queue
@@ -469,8 +469,16 @@ void ProductionUpdate::cancelUnitCreate( ProductionID productionID )
469469
if( production->m_productionID == productionID )
470470
{
471471

472-
// give the player the cost of the object back
473472
Player *player = getObject()->getControllingPlayer();
473+
#if !RETAIL_COMPATIBLE_CRC
474+
// TheSuperHackers @bugfix arcticdolphin 08/03/2026 Prevent cancel once units are produced to avoid free unit exploit
475+
if( !forceCancel && production->getProductionQuantityRemaining() < production->getProductionQuantity() )
476+
{
477+
return;
478+
}
479+
#endif
480+
481+
// give the player the cost of the object back
474482
Money *money = player->getMoney();
475483
money->deposit( production->m_objectToProduce->calcCostToBuild( player ), TRUE, FALSE );
476484

@@ -1140,7 +1148,7 @@ void ProductionUpdate::cancelAndRefundAllProduction()
11401148
if( m_productionQueue )
11411149
{
11421150
if( m_productionQueue->getProductionType() == PRODUCTION_UNIT )
1143-
cancelUnitCreate( m_productionQueue->getProductionID() );
1151+
cancelUnitCreate( m_productionQueue->getProductionID(), TRUE );
11441152
else if( m_productionQueue->getProductionType() == PRODUCTION_UPGRADE )
11451153
cancelUpgrade( m_productionQueue->getProductionUpgrade() );
11461154
else

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ class ProductionUpdateInterface
158158
virtual UnsignedInt countUnitTypeInQueue( const ThingTemplate *unitType ) const = 0;
159159

160160
virtual Bool queueCreateUnit( const ThingTemplate *unitType, ProductionID productionID ) = 0;
161-
virtual void cancelUnitCreate( ProductionID productionID ) = 0;
161+
virtual void cancelUnitCreate( ProductionID productionID, Bool forceCancel = FALSE ) = 0;
162162
virtual void cancelAllUnitsOfType( const ThingTemplate *unitType) = 0;
163163

164164
virtual void cancelAndRefundAllProduction() = 0;
@@ -214,7 +214,7 @@ class ProductionUpdate : public UpdateModule, public ProductionUpdateInterface,
214214
virtual UnsignedInt countUnitTypeInQueue( const ThingTemplate *unitType ) const; ///< count number of units with matching unit type in the production queue
215215

216216
virtual Bool queueCreateUnit( const ThingTemplate *unitType, ProductionID productionID ); ///< queue unit to be produced
217-
virtual void cancelUnitCreate( ProductionID productionID ); ///< cancel construction of unit with matching production ID
217+
virtual void cancelUnitCreate( ProductionID productionID, Bool forceCancel = FALSE ); ///< cancel construction of unit with matching production ID
218218
virtual void cancelAllUnitsOfType( const ThingTemplate *unitType); ///< cancel all production of type unitType
219219

220220
virtual void cancelAndRefundAllProduction(); ///< cancel and refund anything in the production queue

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

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,7 @@ Bool ProductionUpdate::queueCreateUnit( const ThingTemplate *unitType, Productio
458458
//-------------------------------------------------------------------------------------------------
459459
/** Cancel the construction of the unit with the matching production ID */
460460
//-------------------------------------------------------------------------------------------------
461-
void ProductionUpdate::cancelUnitCreate( ProductionID productionID )
461+
void ProductionUpdate::cancelUnitCreate( ProductionID productionID, Bool forceCancel )
462462
{
463463

464464
// search for the production entry in our queue
@@ -470,8 +470,16 @@ void ProductionUpdate::cancelUnitCreate( ProductionID productionID )
470470
if( production->m_productionID == productionID )
471471
{
472472

473-
// give the player the cost of the object back
474473
Player *player = getObject()->getControllingPlayer();
474+
#if !RETAIL_COMPATIBLE_CRC
475+
// TheSuperHackers @bugfix arcticdolphin 08/03/2026 Prevent cancel once units are produced to avoid free unit exploit
476+
if( !forceCancel && production->getProductionQuantityRemaining() < production->getProductionQuantity() )
477+
{
478+
return;
479+
}
480+
#endif
481+
482+
// give the player the cost of the object back
475483
Money *money = player->getMoney();
476484
money->deposit( production->m_objectToProduce->calcCostToBuild( player ), TRUE, FALSE );
477485

@@ -1145,7 +1153,7 @@ void ProductionUpdate::cancelAndRefundAllProduction()
11451153
if( m_productionQueue )
11461154
{
11471155
if( m_productionQueue->getProductionType() == PRODUCTION_UNIT )
1148-
cancelUnitCreate( m_productionQueue->getProductionID() );
1156+
cancelUnitCreate( m_productionQueue->getProductionID(), TRUE );
11491157
else if( m_productionQueue->getProductionType() == PRODUCTION_UPGRADE )
11501158
cancelUpgrade( m_productionQueue->getProductionUpgrade() );
11511159
else

0 commit comments

Comments
 (0)