diff --git a/Generals/Code/GameEngine/Include/GameLogic/Module/ProductionUpdate.h b/Generals/Code/GameEngine/Include/GameLogic/Module/ProductionUpdate.h index 3860bfc4349..7cacd003bf7 100644 --- a/Generals/Code/GameEngine/Include/GameLogic/Module/ProductionUpdate.h +++ b/Generals/Code/GameEngine/Include/GameLogic/Module/ProductionUpdate.h @@ -153,7 +153,7 @@ class ProductionUpdateInterface virtual ProductionID requestUniqueUnitID() = 0; virtual Bool queueUpgrade( const UpgradeTemplate *upgrade ) = 0; - virtual void cancelUpgrade( const UpgradeTemplate *upgrade ) = 0; + virtual Bool cancelUpgrade( const UpgradeTemplate *upgrade ) = 0; virtual Bool isUpgradeInQueue( const UpgradeTemplate *upgrade ) const = 0; virtual UnsignedInt countUnitTypeInQueue( const ThingTemplate *unitType ) const = 0; @@ -209,7 +209,7 @@ class ProductionUpdate : public UpdateModule, public ProductionUpdateInterface, virtual ProductionID requestUniqueUnitID() override { ProductionID tmp = m_uniqueID; m_uniqueID = (ProductionID)(m_uniqueID+1); return tmp; } virtual Bool queueUpgrade( const UpgradeTemplate *upgrade ) override; ///< queue upgrade "research" - virtual void cancelUpgrade( const UpgradeTemplate *upgrade ) override; ///< cancel upgrade "research" + virtual Bool cancelUpgrade( const UpgradeTemplate *upgrade ) override; ///< cancel upgrade "research" virtual Bool isUpgradeInQueue( const UpgradeTemplate *upgrade ) const override; ///< is the upgrade in our production queue already virtual UnsignedInt countUnitTypeInQueue( const ThingTemplate *unitType ) const override; ///< count number of units with matching unit type in the production queue @@ -239,10 +239,12 @@ class ProductionUpdate : public UpdateModule, public ProductionUpdateInterface, protected: - void addToProductionQueue( ProductionEntry *production ); ///< add to *END* of production queue list void removeFromProductionQueue( ProductionEntry *production ); ///< remove production from the queue list + Bool cancelUpgrade( ProductionEntry *production ); + Bool cancelUnitCreate( ProductionEntry *production ); + void updateDoors(); ///< update the door behavior struct DoorInfo diff --git a/Generals/Code/GameEngine/Source/GameLogic/Object/Update/ProductionUpdate.cpp b/Generals/Code/GameEngine/Source/GameLogic/Object/Update/ProductionUpdate.cpp index f182e1d38fd..50292c62045 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/Object/Update/ProductionUpdate.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/Object/Update/ProductionUpdate.cpp @@ -323,19 +323,11 @@ Bool ProductionUpdate::queueUpgrade( const UpgradeTemplate *upgrade ) //------------------------------------------------------------------------------------------------- /** Cancel an upgrade being produced here */ //------------------------------------------------------------------------------------------------- -void ProductionUpdate::cancelUpgrade( const UpgradeTemplate *upgrade ) +Bool ProductionUpdate::cancelUpgrade( const UpgradeTemplate *upgrade ) { - // sanity if( upgrade == nullptr ) - return; - - // get the player - Player *player = getObject()->getControllingPlayer(); - - // sanity, you can't cancel it if the player isn't actually building one - if( upgrade->getUpgradeType() == UPGRADE_TYPE_PLAYER && player->hasUpgradeInProduction( upgrade ) == FALSE ) - return; + return FALSE; // // find the production entry for this upgrade in the queue here, there can only be one @@ -344,34 +336,15 @@ void ProductionUpdate::cancelUpgrade( const UpgradeTemplate *upgrade ) ProductionEntry *production; for( production = m_productionQueue; production; production = production->m_next ) { - if( production->m_type == PRODUCTION_UPGRADE && production->m_upgradeToResearch == upgrade ) - break; + { + return cancelUpgrade( production ); + } } - // sanity, entry not found - if( production == nullptr ) - return; - - // refund money back to the player - Money *money = player->getMoney(); - money->deposit( production->m_upgradeToResearch->calcCostToBuild( player ), TRUE, FALSE ); - - // remove this production from the queue - removeFromProductionQueue( production ); - - // delete production instance - deleteInstance(production); - - // - // remove the IN_PRODUCTION status of this upgrade from the player, object upgrades don't - // have any other IN_PRODUCTION status other than their existence in the build queue - // - if( upgrade->getUpgradeType() == UPGRADE_TYPE_PLAYER ) - player->removeUpgrade( upgrade ); - + return FALSE; } //------------------------------------------------------------------------------------------------- @@ -465,33 +438,11 @@ Bool ProductionUpdate::cancelUnitCreate( ProductionID productionID ) ProductionEntry *production; for( production = m_productionQueue; production; production = production->m_next ) { - // are we at the one we want get rid of it if( production->m_productionID == productionID ) { - - Player *player = getObject()->getControllingPlayer(); - -#if !RETAIL_COMPATIBLE_CRC - // TheSuperHackers @bugfix arcticdolphin 07/09/2026 No cancel once the batch has started producing. - if( production->getProductionQuantityRemaining() < production->getProductionQuantity() ) - return FALSE; -#endif - - // give the player the cost of the object back - Money *money = player->getMoney(); - money->deposit( production->m_objectToProduce->calcCostToBuild( player ), TRUE, FALSE ); - - // remove from queue list - removeFromProductionQueue( production ); - - // delete the production entry - deleteInstance(production); - - return TRUE; - + return cancelUnitCreate( production ); } - } return FALSE; @@ -516,7 +467,7 @@ void ProductionUpdate::cancelAllUnitsOfType( const ThingTemplate *unitType) ProductionEntry *temp = production->m_next; // cancel the production - cancelUnitCreate( production->getProductionID() ); + cancelUnitCreate( production ); // advance production = temp; @@ -702,7 +653,7 @@ UpdateSleepTime ProductionUpdate::update() // Don't cancel dozers in the queue. jba. if (!production->getProductionObject()->isKindOf(KINDOF_DOZER)) { - if( cancelUnitCreate(production->getProductionID()) ) + if( cancelUnitCreate(production) ) return UPDATE_SLEEP_NONE; } @@ -1091,6 +1042,66 @@ void ProductionUpdate::removeFromProductionQueue( ProductionEntry *production ) } +//------------------------------------------------------------------------------------------------- +Bool ProductionUpdate::cancelUpgrade( ProductionEntry *production ) +{ + DEBUG_ASSERTCRASH(production->getProductionType() == PRODUCTION_UPGRADE, + ("ProductionUpdate::cancelUpgrade - called with wrong production type")); + + Player *player = getObject()->getControllingPlayer(); + const UpgradeTemplate *upgrade = production->getProductionUpgrade(); + + // sanity, you can't cancel it if the player isn't actually building one + if( upgrade->getUpgradeType() == UPGRADE_TYPE_PLAYER && player->hasUpgradeInProduction( upgrade ) == FALSE ) + return FALSE; + + // refund money back to the player + Money *money = player->getMoney(); + money->deposit( production->m_upgradeToResearch->calcCostToBuild( player ), TRUE, FALSE ); + + // remove this production from the queue + removeFromProductionQueue( production ); + + // delete production instance + deleteInstance(production); + + // + // remove the IN_PRODUCTION status of this upgrade from the player, object upgrades don't + // have any other IN_PRODUCTION status other than their existence in the build queue + // + if( upgrade->getUpgradeType() == UPGRADE_TYPE_PLAYER ) + player->removeUpgrade( upgrade ); + + return TRUE; +} + +//------------------------------------------------------------------------------------------------- +Bool ProductionUpdate::cancelUnitCreate( ProductionEntry *production ) +{ + DEBUG_ASSERTCRASH(production->getProductionType() == PRODUCTION_UNIT, + ("ProductionUpdate::cancelUnitCreate - called with wrong production type")); + + Player *player = getObject()->getControllingPlayer(); + +#if !RETAIL_COMPATIBLE_CRC + // TheSuperHackers @bugfix arcticdolphin 07/09/2026 No cancel once the batch has started producing. + if( production->getProductionQuantityRemaining() < production->getProductionQuantity() ) + return FALSE; +#endif + + // give the player the cost of the object back + Money *money = player->getMoney(); + money->deposit( production->m_objectToProduce->calcCostToBuild( player ), TRUE, FALSE ); + + // remove from queue list + removeFromProductionQueue( production ); + + // delete the production entry + deleteInstance(production); + + return TRUE; +} + //------------------------------------------------------------------------------------------------- /** Is the upgrade already in the production queue. Note that you can only have one * production entry for any given upgrade in the queue */ @@ -1139,36 +1150,46 @@ void ProductionUpdate::onDie( const DamageInfo *damageInfo ) // ------------------------------------------------------------------------------------------------ void ProductionUpdate::cancelAndRefundAllProduction() { - // Empirically, in release the code can loop forever. So we limit to 100 passes. jba. [8/31/2003] - const Int productionLimit = 100;// With luck, we never queue up 100 units. [8/31/2003] + // Empirically, in release the code can loop forever. So we limit to 100 passes. jba. [8/31/2003] + const Int productionLimit = 100;// With luck, we never queue up 100 units. [8/31/2003] + Int i; + for(i = 0; i < productionLimit && m_productionQueue; ++i) + { + ProductionEntry *production = m_productionQueue; - Int i = 0; - ProductionEntry *production = m_productionQueue; - while( production != nullptr && i < productionLimit ) - { - ProductionEntry *nextProduction = production->m_next; - - if( production->getProductionType() == PRODUCTION_UNIT ) - { - if( !cancelUnitCreate( production->getProductionID() ) ) - { - removeFromProductionQueue( production ); - deleteInstance( production ); - } - } - else if( production->getProductionType() == PRODUCTION_UPGRADE ) - cancelUpgrade( production->getProductionUpgrade() ); - else - { - // unknown production type - DEBUG_CRASH(( "ProductionUpdate::cancelAndRefundAllProduction - Unknown production type '%d'", - production->getProductionType() )); - return; - } - - production = nextProduction; - ++i; - } + if( production->getProductionType() == PRODUCTION_UNIT ) + { + if( !cancelUnitCreate( production ) ) + { +#if RETAIL_COMPATIBLE_CRC + DEBUG_CRASH(("This code path cannot be reached")); +#endif + // Unable to cancel for a refund... delete anyway. + removeFromProductionQueue( production ); + deleteInstance( production ); + } + } + else if( production->getProductionType() == PRODUCTION_UPGRADE ) + { + if( !cancelUpgrade( production ) ) + { +#if RETAIL_COMPATIBLE_CRC + // Cannot cancel the head production... this loop is stuck now and can quit. + return; +#else + // Unable to cancel for a refund... delete anyway. + removeFromProductionQueue( production ); + deleteInstance( production ); +#endif + } + } + else + { + // unknown production type + DEBUG_CRASH(( "ProductionUpdate::cancelAndRefundAllProduction - Unknown production type '%d'", production->getProductionType() )); + return; + } + } } // ------------------------------------------------------------------------------------------------ diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/ProductionUpdate.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/ProductionUpdate.h index 82444f91a4c..1bd6e5a3985 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/ProductionUpdate.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/ProductionUpdate.h @@ -153,7 +153,7 @@ class ProductionUpdateInterface virtual ProductionID requestUniqueUnitID() = 0; virtual Bool queueUpgrade( const UpgradeTemplate *upgrade ) = 0; - virtual void cancelUpgrade( const UpgradeTemplate *upgrade ) = 0; + virtual Bool cancelUpgrade( const UpgradeTemplate *upgrade ) = 0; virtual Bool isUpgradeInQueue( const UpgradeTemplate *upgrade ) const = 0; virtual UnsignedInt countUnitTypeInQueue( const ThingTemplate *unitType ) const = 0; @@ -209,7 +209,7 @@ class ProductionUpdate : public UpdateModule, public ProductionUpdateInterface, virtual ProductionID requestUniqueUnitID() override { ProductionID tmp = m_uniqueID; m_uniqueID = (ProductionID)(m_uniqueID+1); return tmp; } virtual Bool queueUpgrade( const UpgradeTemplate *upgrade ) override; ///< queue upgrade "research" - virtual void cancelUpgrade( const UpgradeTemplate *upgrade ) override; ///< cancel upgrade "research" + virtual Bool cancelUpgrade( const UpgradeTemplate *upgrade ) override; ///< cancel upgrade "research" virtual Bool isUpgradeInQueue( const UpgradeTemplate *upgrade ) const override; ///< is the upgrade in our production queue already virtual UnsignedInt countUnitTypeInQueue( const ThingTemplate *unitType ) const override; ///< count number of units with matching unit type in the production queue @@ -239,10 +239,12 @@ class ProductionUpdate : public UpdateModule, public ProductionUpdateInterface, protected: - void addToProductionQueue( ProductionEntry *production ); ///< add to *END* of production queue list void removeFromProductionQueue( ProductionEntry *production ); ///< remove production from the queue list + Bool cancelUpgrade( ProductionEntry *production ); + Bool cancelUnitCreate( ProductionEntry *production ); + void updateDoors(); ///< update the door behavior struct DoorInfo diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/ProductionUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/ProductionUpdate.cpp index 204bbdd1a38..c583de7dba3 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/ProductionUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/ProductionUpdate.cpp @@ -323,19 +323,11 @@ Bool ProductionUpdate::queueUpgrade( const UpgradeTemplate *upgrade ) //------------------------------------------------------------------------------------------------- /** Cancel an upgrade being produced here */ //------------------------------------------------------------------------------------------------- -void ProductionUpdate::cancelUpgrade( const UpgradeTemplate *upgrade ) +Bool ProductionUpdate::cancelUpgrade( const UpgradeTemplate *upgrade ) { - // sanity if( upgrade == nullptr ) - return; - - // get the player - Player *player = getObject()->getControllingPlayer(); - - // sanity, you can't cancel it if the player isn't actually building one - if( upgrade->getUpgradeType() == UPGRADE_TYPE_PLAYER && player->hasUpgradeInProduction( upgrade ) == FALSE ) - return; + return FALSE; // // find the production entry for this upgrade in the queue here, there can only be one @@ -344,34 +336,15 @@ void ProductionUpdate::cancelUpgrade( const UpgradeTemplate *upgrade ) ProductionEntry *production; for( production = m_productionQueue; production; production = production->m_next ) { - if( production->m_type == PRODUCTION_UPGRADE && production->m_upgradeToResearch == upgrade ) - break; + { + return cancelUpgrade( production ); + } } - // sanity, entry not found - if( production == nullptr ) - return; - - // refund money back to the player - Money *money = player->getMoney(); - money->deposit( production->m_upgradeToResearch->calcCostToBuild( player ), TRUE, FALSE ); - - // remove this production from the queue - removeFromProductionQueue( production ); - - // delete production instance - deleteInstance(production); - - // - // remove the IN_PRODUCTION status of this upgrade from the player, object upgrades don't - // have any other IN_PRODUCTION status other than their existence in the build queue - // - if( upgrade->getUpgradeType() == UPGRADE_TYPE_PLAYER ) - player->removeUpgrade( upgrade ); - + return FALSE; } //------------------------------------------------------------------------------------------------- @@ -465,33 +438,11 @@ Bool ProductionUpdate::cancelUnitCreate( ProductionID productionID ) ProductionEntry *production; for( production = m_productionQueue; production; production = production->m_next ) { - // are we at the one we want get rid of it if( production->m_productionID == productionID ) { - - Player *player = getObject()->getControllingPlayer(); - -#if !RETAIL_COMPATIBLE_CRC - // TheSuperHackers @bugfix arcticdolphin 07/09/2026 No cancel once the batch has started producing. - if( production->getProductionQuantityRemaining() < production->getProductionQuantity() ) - return FALSE; -#endif - - // give the player the cost of the object back - Money *money = player->getMoney(); - money->deposit( production->m_objectToProduce->calcCostToBuild( player ), TRUE, FALSE ); - - // remove from queue list - removeFromProductionQueue( production ); - - // delete the production entry - deleteInstance(production); - - return TRUE; - + return cancelUnitCreate( production ); } - } return FALSE; @@ -516,7 +467,7 @@ void ProductionUpdate::cancelAllUnitsOfType( const ThingTemplate *unitType) ProductionEntry *temp = production->m_next; // cancel the production - cancelUnitCreate( production->getProductionID() ); + cancelUnitCreate( production ); // advance production = temp; @@ -702,7 +653,7 @@ UpdateSleepTime ProductionUpdate::update() // Don't cancel dozers in the queue. jba. if (!production->getProductionObject()->isKindOf(KINDOF_DOZER)) { - if( cancelUnitCreate(production->getProductionID()) ) + if( cancelUnitCreate(production) ) return UPDATE_SLEEP_NONE; } @@ -1095,6 +1046,66 @@ void ProductionUpdate::removeFromProductionQueue( ProductionEntry *production ) } +//------------------------------------------------------------------------------------------------- +Bool ProductionUpdate::cancelUpgrade( ProductionEntry *production ) +{ + DEBUG_ASSERTCRASH(production->getProductionType() == PRODUCTION_UPGRADE, + ("ProductionUpdate::cancelUpgrade - called with wrong production type")); + + Player *player = getObject()->getControllingPlayer(); + const UpgradeTemplate *upgrade = production->getProductionUpgrade(); + + // sanity, you can't cancel it if the player isn't actually building one + if( upgrade->getUpgradeType() == UPGRADE_TYPE_PLAYER && player->hasUpgradeInProduction( upgrade ) == FALSE ) + return FALSE; + + // refund money back to the player + Money *money = player->getMoney(); + money->deposit( production->m_upgradeToResearch->calcCostToBuild( player ), TRUE, FALSE ); + + // remove this production from the queue + removeFromProductionQueue( production ); + + // delete production instance + deleteInstance(production); + + // + // remove the IN_PRODUCTION status of this upgrade from the player, object upgrades don't + // have any other IN_PRODUCTION status other than their existence in the build queue + // + if( upgrade->getUpgradeType() == UPGRADE_TYPE_PLAYER ) + player->removeUpgrade( upgrade ); + + return TRUE; +} + +//------------------------------------------------------------------------------------------------- +Bool ProductionUpdate::cancelUnitCreate( ProductionEntry *production ) +{ + DEBUG_ASSERTCRASH(production->getProductionType() == PRODUCTION_UNIT, + ("ProductionUpdate::cancelUnitCreate - called with wrong production type")); + + Player *player = getObject()->getControllingPlayer(); + +#if !RETAIL_COMPATIBLE_CRC + // TheSuperHackers @bugfix arcticdolphin 07/09/2026 No cancel once the batch has started producing. + if( production->getProductionQuantityRemaining() < production->getProductionQuantity() ) + return FALSE; +#endif + + // give the player the cost of the object back + Money *money = player->getMoney(); + money->deposit( production->m_objectToProduce->calcCostToBuild( player ), TRUE, FALSE ); + + // remove from queue list + removeFromProductionQueue( production ); + + // delete the production entry + deleteInstance(production); + + return TRUE; +} + //------------------------------------------------------------------------------------------------- /** Is the upgrade already in the production queue. Note that you can only have one * production entry for any given upgrade in the queue */ @@ -1145,32 +1156,43 @@ void ProductionUpdate::cancelAndRefundAllProduction() { // Empirically, in release the code can loop forever. So we limit to 100 passes. jba. [8/31/2003] const Int productionLimit = 100;// With luck, we never queue up 100 units. [8/31/2003] - - Int i = 0; - ProductionEntry *production = m_productionQueue; - while( production != nullptr && i < productionLimit ) + Int i; + for(i = 0; i < productionLimit && m_productionQueue; ++i) { - ProductionEntry *nextProduction = production->m_next; + ProductionEntry *production = m_productionQueue; if( production->getProductionType() == PRODUCTION_UNIT ) { - if( !cancelUnitCreate( production->getProductionID() ) ) + if( !cancelUnitCreate( production ) ) { +#if RETAIL_COMPATIBLE_CRC + DEBUG_CRASH(("This code path cannot be reached")); +#endif + // Unable to cancel for a refund... delete anyway. removeFromProductionQueue( production ); deleteInstance( production ); } } else if( production->getProductionType() == PRODUCTION_UPGRADE ) - cancelUpgrade( production->getProductionUpgrade() ); + { + if( !cancelUpgrade( production ) ) + { +#if RETAIL_COMPATIBLE_CRC + // Cannot cancel the head production... this loop is stuck now and can quit. + return; +#else + // Unable to cancel for a refund... delete anyway. + removeFromProductionQueue( production ); + deleteInstance( production ); +#endif + } + } else { // unknown production type DEBUG_CRASH(( "ProductionUpdate::cancelAndRefundAllProduction - Unknown production type '%d'", production->getProductionType() )); return; } - - production = nextProduction; - ++i; } }