Skip to content

Commit cb37ac2

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

4 files changed

Lines changed: 39 additions & 13 deletions

File tree

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,8 @@ 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+
// TheSuperHackers @info arcticdolphin 08/03/2026 Introduced forceCancel param (used to bypass partial-production refund guard)
162+
virtual void cancelUnitCreate( ProductionID productionID, Bool forceCancel = FALSE ) = 0;
162163
virtual void cancelAllUnitsOfType( const ThingTemplate *unitType) = 0;
163164

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

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
212+
virtual Bool queueCreateUnit( const ThingTemplate *unitType, ProductionID productionID ); ///< queue unit to be produced
213+
// TheSuperHackers @info arcticdolphin 08/03/2026 Introduced forceCancel param (used to bypass partial-production refund guard)
214+
virtual void cancelUnitCreate( ProductionID productionID, Bool forceCancel = FALSE ); ///< cancel construction of unit with matching production ID
213215
virtual void cancelAllUnitsOfType( const ThingTemplate *unitType); ///< cancel all production of type unitType
214216

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

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

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -457,7 +457,8 @@ 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+
// TheSuperHackers @info arcticdolphin 08/03/2026 Introduced forceCancel param (used to bypass partial-production refund guard)
461+
void ProductionUpdate::cancelUnitCreate( ProductionID productionID, Bool forceCancel )
461462
{
462463

463464
// search for the production entry in our queue
@@ -469,8 +470,16 @@ void ProductionUpdate::cancelUnitCreate( ProductionID productionID )
469470
if( production->m_productionID == productionID )
470471
{
471472

472-
// give the player the cost of the object back
473473
Player *player = getObject()->getControllingPlayer();
474+
#if !RETAIL_COMPATIBLE_CRC
475+
// TheSuperHackers @bugfix arcticdolphin 08/03/2026 Do not cancel if units were already produced from this entry
476+
if( !forceCancel && production->getProductionQuantityRemaining() < production->getProductionQuantity() )
477+
{
478+
return;
479+
}
480+
#endif
481+
482+
// give the player the cost of the object back
474483
Money *money = player->getMoney();
475484
money->deposit( production->m_objectToProduce->calcCostToBuild( player ), TRUE, FALSE );
476485

@@ -693,7 +702,8 @@ UpdateSleepTime ProductionUpdate::update()
693702
if (!production->getProductionObject()->isKindOf(KINDOF_DOZER))
694703
{
695704

696-
cancelUnitCreate(production->getProductionID());
705+
// TheSuperHackers @info arcticdolphin 08/03/2026 Introduced forceCancel param (used to bypass partial-production refund guard)
706+
cancelUnitCreate(production->getProductionID(), TRUE);
697707
return UPDATE_SLEEP_NONE;
698708

699709
}
@@ -1140,7 +1150,8 @@ void ProductionUpdate::cancelAndRefundAllProduction()
11401150
if( m_productionQueue )
11411151
{
11421152
if( m_productionQueue->getProductionType() == PRODUCTION_UNIT )
1143-
cancelUnitCreate( m_productionQueue->getProductionID() );
1153+
// TheSuperHackers @info arcticdolphin 08/03/2026 Introduced forceCancel param (used to bypass partial-production refund guard)
1154+
cancelUnitCreate( m_productionQueue->getProductionID(), TRUE );
11441155
else if( m_productionQueue->getProductionType() == PRODUCTION_UPGRADE )
11451156
cancelUpgrade( m_productionQueue->getProductionUpgrade() );
11461157
else

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,8 @@ 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+
// TheSuperHackers @info arcticdolphin 08/03/2026 Introduced forceCancel param (used to bypass partial-production refund guard)
162+
virtual void cancelUnitCreate( ProductionID productionID, Bool forceCancel = FALSE ) = 0;
162163
virtual void cancelAllUnitsOfType( const ThingTemplate *unitType) = 0;
163164

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

216217
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
218+
// TheSuperHackers @info arcticdolphin 08/03/2026 Introduced forceCancel param (used to bypass partial-production refund guard)
219+
virtual void cancelUnitCreate( ProductionID productionID, Bool forceCancel = FALSE ); ///< cancel construction of unit with matching production ID
218220
virtual void cancelAllUnitsOfType( const ThingTemplate *unitType); ///< cancel all production of type unitType
219221

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

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

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,8 @@ 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+
// TheSuperHackers @info arcticdolphin 08/03/2026 Introduced forceCancel param (used to bypass partial-production refund guard)
462+
void ProductionUpdate::cancelUnitCreate( ProductionID productionID, Bool forceCancel )
462463
{
463464

464465
// search for the production entry in our queue
@@ -470,8 +471,16 @@ void ProductionUpdate::cancelUnitCreate( ProductionID productionID )
470471
if( production->m_productionID == productionID )
471472
{
472473

473-
// give the player the cost of the object back
474474
Player *player = getObject()->getControllingPlayer();
475+
#if !RETAIL_COMPATIBLE_CRC
476+
// TheSuperHackers @bugfix arcticdolphin 08/03/2026 Do not cancel if units were already produced from this entry
477+
if( !forceCancel && production->getProductionQuantityRemaining() < production->getProductionQuantity() )
478+
{
479+
return;
480+
}
481+
#endif
482+
483+
// give the player the cost of the object back
475484
Money *money = player->getMoney();
476485
money->deposit( production->m_objectToProduce->calcCostToBuild( player ), TRUE, FALSE );
477486

@@ -694,7 +703,8 @@ UpdateSleepTime ProductionUpdate::update()
694703
if (!production->getProductionObject()->isKindOf(KINDOF_DOZER))
695704
{
696705

697-
cancelUnitCreate(production->getProductionID());
706+
// TheSuperHackers @info arcticdolphin 08/03/2026 Introduced forceCancel param (used to bypass partial-production refund guard)
707+
cancelUnitCreate(production->getProductionID(), TRUE);
698708
return UPDATE_SLEEP_NONE;
699709

700710
}
@@ -1145,7 +1155,8 @@ void ProductionUpdate::cancelAndRefundAllProduction()
11451155
if( m_productionQueue )
11461156
{
11471157
if( m_productionQueue->getProductionType() == PRODUCTION_UNIT )
1148-
cancelUnitCreate( m_productionQueue->getProductionID() );
1158+
// TheSuperHackers @info arcticdolphin 08/03/2026 Introduced forceCancel param (used to bypass partial-production refund guard)
1159+
cancelUnitCreate( m_productionQueue->getProductionID(), TRUE );
11491160
else if( m_productionQueue->getProductionType() == PRODUCTION_UPGRADE )
11501161
cancelUpgrade( m_productionQueue->getProductionUpgrade() );
11511162
else

0 commit comments

Comments
 (0)