From 5a61f29d8cd7027bedf174763fb3a622035543d8 Mon Sep 17 00:00:00 2001 From: Stubbjax Date: Tue, 17 Jun 2025 12:45:37 +1000 Subject: [PATCH 1/4] bugfix: Cancel transferred in-progress upgrades the target player already has in progress or completed --- .../GameEngine/Source/Common/RTS/Player.cpp | 35 +++++++++++++++++++ .../GameEngine/Source/Common/RTS/Player.cpp | 34 ++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/Generals/Code/GameEngine/Source/Common/RTS/Player.cpp b/Generals/Code/GameEngine/Source/Common/RTS/Player.cpp index 5cd5b3842ac..b148f2950db 100644 --- a/Generals/Code/GameEngine/Source/Common/RTS/Player.cpp +++ b/Generals/Code/GameEngine/Source/Common/RTS/Player.cpp @@ -96,6 +96,7 @@ #include "GameLogic/Module/SpecialPowerModule.h" #include "GameLogic/Module/SupplyTruckAIUpdate.h" #include "GameLogic/Module/BattlePlanUpdate.h" +#include "GameLogic/Module/ProductionUpdate.h" #include "GameLogic/VictoryConditions.h" #include "GameNetwork/GameInfo.h" @@ -1731,6 +1732,18 @@ void Player::setObjectsEnabled(AsciiString templateTypeToAffect, Bool enable) } } +//============================================================================= +static void cancelUpgradeInProduction(Object* obj, void* userData) +{ + const UpgradeTemplate* upgradeTemplate = (const UpgradeTemplate*)userData; + ProductionUpdateInterface* pui = ProductionUpdate::getProductionUpdateInterfaceFromObject(obj); + + if (pui && pui->isUpgradeInQueue(upgradeTemplate)) + { + pui->cancelUpgrade(upgradeTemplate); + } +} + //============================================================================= void Player::transferAssetsFromThat(Player *that) { @@ -1739,6 +1752,28 @@ void Player::transferAssetsFromThat(Player *that) return; } +#if !RETAIL_COMPATIBLE_CRC + // TheSuperHackers @bugfix Stubbjax 03/02/2026 Cancel any in-progress player upgrades 'that' + // player currently has in progress that 'this' player already has in progress or completed. + std::vector upgradesToCancel; + for (Upgrade* upgrade = that->m_upgradeList; upgrade; upgrade = upgrade->friend_getNext()) + { + const UpgradeTemplate* upgradeTemplate = upgrade->getTemplate(); + + if (upgrade->getStatus() == UPGRADE_STATUS_IN_PRODUCTION + && upgradeTemplate->getUpgradeType() == UPGRADE_TYPE_PLAYER + && (hasUpgradeComplete(upgradeTemplate) || hasUpgradeInProduction(upgradeTemplate))) + { + upgradesToCancel.push_back(upgradeTemplate); + } + } + + for (const UpgradeTemplate* upgradeTemplate : upgradesToCancel) + { + that->iterateObjects(cancelUpgradeInProduction, (void*)upgradeTemplate); + } +#endif + std::list objsToTransfer; // let's not transfer beacons diff --git a/GeneralsMD/Code/GameEngine/Source/Common/RTS/Player.cpp b/GeneralsMD/Code/GameEngine/Source/Common/RTS/Player.cpp index f00971e6164..7316f432254 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/RTS/Player.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/RTS/Player.cpp @@ -2116,6 +2116,18 @@ void Player::setObjectsEnabled(AsciiString templateTypeToAffect, Bool enable) } } +//============================================================================= +static void cancelUpgradeInProduction(Object* obj, void* userData) +{ + const UpgradeTemplate* upgradeTemplate = (const UpgradeTemplate*)userData; + ProductionUpdateInterface* pui = ProductionUpdate::getProductionUpdateInterfaceFromObject(obj); + + if (pui && pui->isUpgradeInQueue(upgradeTemplate)) + { + pui->cancelUpgrade(upgradeTemplate); + } +} + //============================================================================= void Player::transferAssetsFromThat(Player *that) { @@ -2124,6 +2136,28 @@ void Player::transferAssetsFromThat(Player *that) return; } +#if !RETAIL_COMPATIBLE_CRC + // TheSuperHackers @bugfix Stubbjax 03/02/2026 Cancel any in-progress player upgrades 'that' + // player currently has in progress that 'this' player already has in progress or completed. + std::vector upgradesToCancel; + for (Upgrade* upgrade = that->m_upgradeList; upgrade; upgrade = upgrade->friend_getNext()) + { + const UpgradeTemplate* upgradeTemplate = upgrade->getTemplate(); + + if (upgrade->getStatus() == UPGRADE_STATUS_IN_PRODUCTION + && upgradeTemplate->getUpgradeType() == UPGRADE_TYPE_PLAYER + && (hasUpgradeComplete(upgradeTemplate) || hasUpgradeInProduction(upgradeTemplate))) + { + upgradesToCancel.push_back(upgradeTemplate); + } + } + + for (const UpgradeTemplate* upgradeTemplate : upgradesToCancel) + { + that->iterateObjects(cancelUpgradeInProduction, (void*)upgradeTemplate); + } +#endif + std::list objsToTransfer; // let's not transfer beacons From 48b246bd559ea88fd696a1a376462d9df0cc62df Mon Sep 17 00:00:00 2001 From: Stubbjax Date: Wed, 4 Mar 2026 14:52:47 +1100 Subject: [PATCH 2/4] bugfix: Prevent purchasing duplicates of transferred in-progress upgrades --- Generals/Code/GameEngine/Source/Common/RTS/Player.cpp | 4 ++++ GeneralsMD/Code/GameEngine/Source/Common/RTS/Player.cpp | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/Generals/Code/GameEngine/Source/Common/RTS/Player.cpp b/Generals/Code/GameEngine/Source/Common/RTS/Player.cpp index b148f2950db..bd58d7af3b8 100644 --- a/Generals/Code/GameEngine/Source/Common/RTS/Player.cpp +++ b/Generals/Code/GameEngine/Source/Common/RTS/Player.cpp @@ -1772,6 +1772,10 @@ void Player::transferAssetsFromThat(Player *that) { that->iterateObjects(cancelUpgradeInProduction, (void*)upgradeTemplate); } + + // TheSuperHackers @bugfix Stubbjax 03/02/2026 Ensure the in-progress upgrade mask is copied from 'that' + // player to 'this' player to prevent duplicate player upgrades being purchased. + m_upgradesInProgress.set(that->m_upgradesInProgress); #endif std::list objsToTransfer; diff --git a/GeneralsMD/Code/GameEngine/Source/Common/RTS/Player.cpp b/GeneralsMD/Code/GameEngine/Source/Common/RTS/Player.cpp index 7316f432254..c044da4e3d7 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/RTS/Player.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/RTS/Player.cpp @@ -2156,6 +2156,10 @@ void Player::transferAssetsFromThat(Player *that) { that->iterateObjects(cancelUpgradeInProduction, (void*)upgradeTemplate); } + + // TheSuperHackers @bugfix Stubbjax 03/02/2026 Ensure the in-progress upgrade mask is copied from 'that' + // player to 'this' player to prevent duplicate player upgrades being purchased. + m_upgradesInProgress.set(that->m_upgradesInProgress); #endif std::list objsToTransfer; From 8ccd9d388ccda0b38a3c8268defead39b3670377 Mon Sep 17 00:00:00 2001 From: Stubbjax Date: Wed, 18 Mar 2026 21:18:33 +1100 Subject: [PATCH 3/4] fix: Improve VC6 relations --- Generals/Code/GameEngine/Source/Common/RTS/Player.cpp | 3 ++- GeneralsMD/Code/GameEngine/Source/Common/RTS/Player.cpp | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Generals/Code/GameEngine/Source/Common/RTS/Player.cpp b/Generals/Code/GameEngine/Source/Common/RTS/Player.cpp index bd58d7af3b8..73293f9b32b 100644 --- a/Generals/Code/GameEngine/Source/Common/RTS/Player.cpp +++ b/Generals/Code/GameEngine/Source/Common/RTS/Player.cpp @@ -1768,8 +1768,9 @@ void Player::transferAssetsFromThat(Player *that) } } - for (const UpgradeTemplate* upgradeTemplate : upgradesToCancel) + for (std::vector::iterator cancelIt = upgradesToCancel.begin(); cancelIt != upgradesToCancel.end(); ++cancelIt) { + const UpgradeTemplate* upgradeTemplate = *cancelIt; that->iterateObjects(cancelUpgradeInProduction, (void*)upgradeTemplate); } diff --git a/GeneralsMD/Code/GameEngine/Source/Common/RTS/Player.cpp b/GeneralsMD/Code/GameEngine/Source/Common/RTS/Player.cpp index c044da4e3d7..02f36302015 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/RTS/Player.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/RTS/Player.cpp @@ -2152,8 +2152,9 @@ void Player::transferAssetsFromThat(Player *that) } } - for (const UpgradeTemplate* upgradeTemplate : upgradesToCancel) + for (std::vector::iterator cancelIt = upgradesToCancel.begin(); cancelIt != upgradesToCancel.end(); ++cancelIt) { + const UpgradeTemplate* upgradeTemplate = *cancelIt; that->iterateObjects(cancelUpgradeInProduction, (void*)upgradeTemplate); } From 9fc16a4616185e08524482c4765f4cc8a6043d3a Mon Sep 17 00:00:00 2001 From: Stubbjax Date: Wed, 18 Mar 2026 21:18:51 +1100 Subject: [PATCH 4/4] fix: Add static cast --- Generals/Code/GameEngine/Source/Common/RTS/Player.cpp | 2 +- GeneralsMD/Code/GameEngine/Source/Common/RTS/Player.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Generals/Code/GameEngine/Source/Common/RTS/Player.cpp b/Generals/Code/GameEngine/Source/Common/RTS/Player.cpp index 73293f9b32b..94c859f8e88 100644 --- a/Generals/Code/GameEngine/Source/Common/RTS/Player.cpp +++ b/Generals/Code/GameEngine/Source/Common/RTS/Player.cpp @@ -1735,7 +1735,7 @@ void Player::setObjectsEnabled(AsciiString templateTypeToAffect, Bool enable) //============================================================================= static void cancelUpgradeInProduction(Object* obj, void* userData) { - const UpgradeTemplate* upgradeTemplate = (const UpgradeTemplate*)userData; + const UpgradeTemplate* upgradeTemplate = static_cast(userData); ProductionUpdateInterface* pui = ProductionUpdate::getProductionUpdateInterfaceFromObject(obj); if (pui && pui->isUpgradeInQueue(upgradeTemplate)) diff --git a/GeneralsMD/Code/GameEngine/Source/Common/RTS/Player.cpp b/GeneralsMD/Code/GameEngine/Source/Common/RTS/Player.cpp index 02f36302015..3db5000a6be 100644 --- a/GeneralsMD/Code/GameEngine/Source/Common/RTS/Player.cpp +++ b/GeneralsMD/Code/GameEngine/Source/Common/RTS/Player.cpp @@ -2119,7 +2119,7 @@ void Player::setObjectsEnabled(AsciiString templateTypeToAffect, Bool enable) //============================================================================= static void cancelUpgradeInProduction(Object* obj, void* userData) { - const UpgradeTemplate* upgradeTemplate = (const UpgradeTemplate*)userData; + const UpgradeTemplate* upgradeTemplate = static_cast(userData); ProductionUpdateInterface* pui = ProductionUpdate::getProductionUpdateInterfaceFromObject(obj); if (pui && pui->isUpgradeInQueue(upgradeTemplate))