From bec5f125c44e94828448d3a4085715a99caa2b4e Mon Sep 17 00:00:00 2001 From: Stubbjax Date: Wed, 11 Feb 2026 00:46:58 +1100 Subject: [PATCH 1/7] refactor: Abstract multiple alliance logic into separate function --- .../ScriptEngine/VictoryConditions.cpp | 53 +++++++++++-------- .../ScriptEngine/VictoryConditions.cpp | 53 +++++++++++-------- 2 files changed, 60 insertions(+), 46 deletions(-) diff --git a/Generals/Code/GameEngine/Source/GameLogic/ScriptEngine/VictoryConditions.cpp b/Generals/Code/GameEngine/Source/GameLogic/ScriptEngine/VictoryConditions.cpp index 7779e98180e..d1add2466d6 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/ScriptEngine/VictoryConditions.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/ScriptEngine/VictoryConditions.cpp @@ -92,6 +92,8 @@ class VictoryConditions : public VictoryConditionsInterface Bool amIObserver( void ) { return m_isObserver;} ///< Am I an observer?( need this for scripts ) virtual UnsignedInt getEndFrame( void ) { return m_endFrame; } ///< on which frame was the game effectively over? private: + Bool multipleAlliancesExist(void); ///< Are there multiple alliances still alive? + Player* m_players[MAX_PLAYER_COUNT]; Int m_localSlotNum; UnsignedInt m_endFrame; @@ -139,39 +141,44 @@ void VictoryConditions::reset( void ) } //------------------------------------------------------------------------------------------------- -void VictoryConditions::update( void ) +Bool VictoryConditions::multipleAlliancesExist() { - if (!TheRecorder->isMultiplayer() || (m_localSlotNum < 0 && !m_isObserver)) - return; + Player* alive = nullptr; - // Check for a single winning alliance - if (!m_singleAllianceRemaining) + for (Int i = 0; i < MAX_PLAYER_COUNT; ++i) { - Bool multipleAlliances = false; - Player *alive = nullptr; - Player *player; - for (Int i=0; iisMultiplayer() || (m_localSlotNum < 0 && !m_isObserver)) + return; + + // Check for a single winning alliance + if (!m_singleAllianceRemaining) + { + if (!multipleAlliancesExist()) { m_singleAllianceRemaining = true; // don't check again m_endFrame = TheGameLogic->getFrame(); diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/ScriptEngine/VictoryConditions.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/ScriptEngine/VictoryConditions.cpp index ae5a84d361e..fc63130a739 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/ScriptEngine/VictoryConditions.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/ScriptEngine/VictoryConditions.cpp @@ -93,6 +93,8 @@ class VictoryConditions : public VictoryConditionsInterface Bool amIObserver( void ) { return m_isObserver;} ///< Am I an observer?( need this for scripts ) virtual UnsignedInt getEndFrame( void ) { return m_endFrame; } ///< on which frame was the game effectively over? private: + Bool multipleAlliancesExist(void); ///< Are there multiple alliances still alive? + Player* m_players[MAX_PLAYER_COUNT]; Int m_localSlotNum; UnsignedInt m_endFrame; @@ -140,39 +142,44 @@ void VictoryConditions::reset( void ) } //------------------------------------------------------------------------------------------------- -void VictoryConditions::update( void ) +Bool VictoryConditions::multipleAlliancesExist() { - if (!TheRecorder->isMultiplayer() || (m_localSlotNum < 0 && !m_isObserver)) - return; + Player* alive = nullptr; - // Check for a single winning alliance - if (!m_singleAllianceRemaining) + for (Int i = 0; i < MAX_PLAYER_COUNT; ++i) { - Bool multipleAlliances = false; - Player *alive = nullptr; - Player *player; - for (Int i=0; iisMultiplayer() || (m_localSlotNum < 0 && !m_isObserver)) + return; + + // Check for a single winning alliance + if (!m_singleAllianceRemaining) + { + if (!multipleAlliancesExist()) { m_singleAllianceRemaining = true; // don't check again m_endFrame = TheGameLogic->getFrame(); From 87a75a4d53d89dd5f3c14eaa0ab1cb6cad159f53 Mon Sep 17 00:00:00 2001 From: Stubbjax Date: Wed, 11 Feb 2026 04:22:43 +1100 Subject: [PATCH 2/7] bugfix: Save victory status to prevent early exits from resulting in defeat in network matches --- .../ScriptEngine/VictoryConditions.cpp | 48 ++++++++++++++---- .../ScriptEngine/VictoryConditions.cpp | 50 +++++++++++++++---- 2 files changed, 79 insertions(+), 19 deletions(-) diff --git a/Generals/Code/GameEngine/Source/GameLogic/ScriptEngine/VictoryConditions.cpp b/Generals/Code/GameEngine/Source/GameLogic/ScriptEngine/VictoryConditions.cpp index d1add2466d6..5c32debc969 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/ScriptEngine/VictoryConditions.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/ScriptEngine/VictoryConditions.cpp @@ -98,6 +98,7 @@ class VictoryConditions : public VictoryConditionsInterface Int m_localSlotNum; UnsignedInt m_endFrame; Bool m_isDefeated[MAX_PLAYER_COUNT]; + Bool m_isVictorious[MAX_PLAYER_COUNT]; Bool m_localPlayerDefeated; ///< prevents condition from being signaled each frame Bool m_singleAllianceRemaining; ///< prevents condition from being signaled each frame Bool m_isObserver; @@ -129,6 +130,7 @@ void VictoryConditions::reset( void ) { m_players[i] = nullptr; m_isDefeated[i] = false; + m_isVictorious[i] = false; } m_localSlotNum = -1; @@ -182,6 +184,29 @@ void VictoryConditions::update( void ) { m_singleAllianceRemaining = true; // don't check again m_endFrame = TheGameLogic->getFrame(); + + // TheSuperHackers @bugfix Stubbjax 11/02/2026 Cache victory status so that premature exits don't void the victory. + + Player* victoriousPlayer = nullptr; + for (Int i = 0; i < MAX_PLAYER_COUNT; ++i) + { + Player* player = m_players[i]; + if (player && !hasSinglePlayerBeenDefeated(player)) + { + victoriousPlayer = player; + break; + } + } + + if (victoriousPlayer) + { + for (Int i = 0; i < MAX_PLAYER_COUNT; ++i) + { + Player* player = m_players[i]; + if (player == victoriousPlayer || (player && areAllies(player, victoriousPlayer))) + m_isVictorious[i] = true; + } + } } } @@ -246,14 +271,13 @@ Bool VictoryConditions::hasAchievedVictory(Player *player) if (!player) return false; - if (m_singleAllianceRemaining) + if (!m_singleAllianceRemaining) + return false; + + for (Int i = 0; i < MAX_PLAYER_COUNT; ++i) { - for (Int i=0; igetFrame(); + + // TheSuperHackers @bugfix Stubbjax 11/02/2026 Cache victory status so that premature exits don't void the victory. + + Player* victoriousPlayer = nullptr; + for (Int i = 0; i < MAX_PLAYER_COUNT; ++i) + { + Player* player = m_players[i]; + if (player && !hasSinglePlayerBeenDefeated(player)) + { + victoriousPlayer = player; + break; + } + } + + if (victoriousPlayer) + { + for (Int i = 0; i < MAX_PLAYER_COUNT; ++i) + { + Player* player = m_players[i]; + if (player == victoriousPlayer || (player && areAllies(player, victoriousPlayer))) + m_isVictorious[i] = true; + } + } } } @@ -248,14 +273,13 @@ Bool VictoryConditions::hasAchievedVictory(Player *player) if (!player) return false; - if (m_singleAllianceRemaining) + if (!m_singleAllianceRemaining) + return false; + + for (Int i = 0; i < MAX_PLAYER_COUNT; ++i) { - for (Int i=0; i Date: Thu, 12 Feb 2026 01:08:41 +1100 Subject: [PATCH 3/7] tweak: Standardise win and loss conditions with streak conditions --- .../GameClient/GUI/GUICallbacks/Menus/ScoreScreen.cpp | 6 +++--- .../GameClient/GUI/GUICallbacks/Menus/ScoreScreen.cpp | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/ScoreScreen.cpp b/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/ScoreScreen.cpp index c662f0cf230..e9e38a7adf6 100644 --- a/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/ScoreScreen.cpp +++ b/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/ScoreScreen.cpp @@ -1597,13 +1597,13 @@ winName.format("ScoreScreen.wnd:StaticTextScore%d", pos); { ++stats.discons[ptIdx]; } - else if (TheVictoryConditions->isLocalAlliedDefeat() || !TheVictoryConditions->getEndFrame()) + else if (TheVictoryConditions->isLocalAlliedVictory()) { - ++stats.losses[ptIdx]; + ++stats.wins[ptIdx]; } else { - ++stats.wins[ptIdx]; + ++stats.losses[ptIdx]; } ScoreKeeper *s = player->getScoreKeeper(); diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/ScoreScreen.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/ScoreScreen.cpp index 208af49523c..3d1beac6bac 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/ScoreScreen.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/ScoreScreen.cpp @@ -1864,13 +1864,13 @@ winName.format("ScoreScreen.wnd:StaticTextScore%d", pos); { ++stats.discons[ptIdx]; } - else if (TheVictoryConditions->isLocalAlliedDefeat() || !TheVictoryConditions->getEndFrame()) + else if (TheVictoryConditions->isLocalAlliedVictory()) { - ++stats.losses[ptIdx]; + ++stats.wins[ptIdx]; } else { - ++stats.wins[ptIdx]; + ++stats.losses[ptIdx]; } ScoreKeeper *s = player->getScoreKeeper(); From aa5f3636bb3bcd4b0ed87d24c3a91de2391ecb70 Mon Sep 17 00:00:00 2001 From: Stubbjax Date: Thu, 12 Feb 2026 01:11:57 +1100 Subject: [PATCH 4/7] refactor: Consolidate duplicate stat conditions --- .../GUI/GUICallbacks/Menus/ScoreScreen.cpp | 49 ++++++++----------- .../GUI/GUICallbacks/Menus/ScoreScreen.cpp | 49 ++++++++----------- 2 files changed, 40 insertions(+), 58 deletions(-) diff --git a/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/ScoreScreen.cpp b/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/ScoreScreen.cpp index e9e38a7adf6..82656fd4c3e 100644 --- a/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/ScoreScreen.cpp +++ b/Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/ScoreScreen.cpp @@ -1592,36 +1592,7 @@ winName.format("ScoreScreen.wnd:StaticTextScore%d", pos); if (TheNetwork->sawCRCMismatch()) { ++stats.desyncs[ptIdx]; - } - else if (gameEndedInDisconnect) - { - ++stats.discons[ptIdx]; - } - else if (TheVictoryConditions->isLocalAlliedVictory()) - { - ++stats.wins[ptIdx]; - } - else - { - ++stats.losses[ptIdx]; - } - ScoreKeeper *s = player->getScoreKeeper(); - stats.buildingsBuilt[ptIdx] += s->getTotalBuildingsBuilt(); - stats.buildingsKilled[ptIdx] += s->getTotalBuildingsDestroyed(); - stats.buildingsLost[ptIdx] += s->getTotalBuildingsLost(); - - if (TheGameSpyGame->isQMGame()) - { - stats.QMGames[ptIdx]++; - } - else - { - stats.customGames[ptIdx]++; - } - - if (TheNetwork->sawCRCMismatch()) - { stats.lossesInARow = 0; stats.desyncsInARow++; stats.disconsInARow = 0; @@ -1630,6 +1601,8 @@ winName.format("ScoreScreen.wnd:StaticTextScore%d", pos); } else if (gameEndedInDisconnect) { + ++stats.discons[ptIdx]; + stats.lossesInARow = 0; stats.desyncsInARow = 0; stats.disconsInARow++; @@ -1638,6 +1611,8 @@ winName.format("ScoreScreen.wnd:StaticTextScore%d", pos); } else if (TheVictoryConditions->isLocalAlliedVictory()) { + ++stats.wins[ptIdx]; + stats.lossesInARow = 0; stats.desyncsInARow = 0; stats.disconsInARow = 0; @@ -1646,6 +1621,8 @@ winName.format("ScoreScreen.wnd:StaticTextScore%d", pos); } else { + ++stats.losses[ptIdx]; + stats.lossesInARow++; stats.desyncsInARow = 0; stats.disconsInARow = 0; @@ -1653,6 +1630,20 @@ winName.format("ScoreScreen.wnd:StaticTextScore%d", pos); stats.maxLossesInARow = max(stats.lossesInARow, stats.maxLossesInARow); } + ScoreKeeper *s = player->getScoreKeeper(); + stats.buildingsBuilt[ptIdx] += s->getTotalBuildingsBuilt(); + stats.buildingsKilled[ptIdx] += s->getTotalBuildingsDestroyed(); + stats.buildingsLost[ptIdx] += s->getTotalBuildingsLost(); + + if (TheGameSpyGame->isQMGame()) + { + stats.QMGames[ptIdx]++; + } + else + { + stats.customGames[ptIdx]++; + } + stats.earnings[ptIdx] += s->getTotalMoneyEarned(); stats.duration[ptIdx] += TheGameLogic->getFrame() / LOGICFRAMES_PER_SECOND / 60; // in minutes stats.games[ptIdx]++; diff --git a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/ScoreScreen.cpp b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/ScoreScreen.cpp index 3d1beac6bac..5199b72619a 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/ScoreScreen.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/ScoreScreen.cpp @@ -1859,36 +1859,7 @@ winName.format("ScoreScreen.wnd:StaticTextScore%d", pos); if (TheNetwork->sawCRCMismatch()) { ++stats.desyncs[ptIdx]; - } - else if (gameEndedInDisconnect) - { - ++stats.discons[ptIdx]; - } - else if (TheVictoryConditions->isLocalAlliedVictory()) - { - ++stats.wins[ptIdx]; - } - else - { - ++stats.losses[ptIdx]; - } - ScoreKeeper *s = player->getScoreKeeper(); - stats.buildingsBuilt[ptIdx] += s->getTotalBuildingsBuilt(); - stats.buildingsKilled[ptIdx] += s->getTotalBuildingsDestroyed(); - stats.buildingsLost[ptIdx] += s->getTotalBuildingsLost(); - - if (TheGameSpyGame->isQMGame()) - { - stats.QMGames[ptIdx]++; - } - else - { - stats.customGames[ptIdx]++; - } - - if (TheNetwork->sawCRCMismatch()) - { stats.lossesInARow = 0; stats.desyncsInARow++; stats.disconsInARow = 0; @@ -1897,6 +1868,8 @@ winName.format("ScoreScreen.wnd:StaticTextScore%d", pos); } else if (gameEndedInDisconnect) { + ++stats.discons[ptIdx]; + stats.lossesInARow = 0; stats.desyncsInARow = 0; stats.disconsInARow++; @@ -1905,6 +1878,8 @@ winName.format("ScoreScreen.wnd:StaticTextScore%d", pos); } else if (TheVictoryConditions->isLocalAlliedVictory()) { + ++stats.wins[ptIdx]; + stats.lossesInARow = 0; stats.desyncsInARow = 0; stats.disconsInARow = 0; @@ -1913,6 +1888,8 @@ winName.format("ScoreScreen.wnd:StaticTextScore%d", pos); } else { + ++stats.losses[ptIdx]; + stats.lossesInARow++; stats.desyncsInARow = 0; stats.disconsInARow = 0; @@ -1920,6 +1897,20 @@ winName.format("ScoreScreen.wnd:StaticTextScore%d", pos); stats.maxLossesInARow = max(stats.lossesInARow, stats.maxLossesInARow); } + ScoreKeeper *s = player->getScoreKeeper(); + stats.buildingsBuilt[ptIdx] += s->getTotalBuildingsBuilt(); + stats.buildingsKilled[ptIdx] += s->getTotalBuildingsDestroyed(); + stats.buildingsLost[ptIdx] += s->getTotalBuildingsLost(); + + if (TheGameSpyGame->isQMGame()) + { + stats.QMGames[ptIdx]++; + } + else + { + stats.customGames[ptIdx]++; + } + stats.earnings[ptIdx] += s->getTotalMoneyEarned(); stats.duration[ptIdx] += TheGameLogic->getFrame() / LOGICFRAMES_PER_SECOND / 60; // in minutes stats.games[ptIdx]++; From 5745099255ee5fbadef4d93a8e6116ad8697ce2f Mon Sep 17 00:00:00 2001 From: Stubbjax Date: Tue, 17 Feb 2026 10:51:44 +1100 Subject: [PATCH 5/7] perf: Minor optimisation --- .../ScriptEngine/VictoryConditions.cpp | 18 ++++++++++++++---- .../ScriptEngine/VictoryConditions.cpp | 18 ++++++++++++++---- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/Generals/Code/GameEngine/Source/GameLogic/ScriptEngine/VictoryConditions.cpp b/Generals/Code/GameEngine/Source/GameLogic/ScriptEngine/VictoryConditions.cpp index 5c32debc969..744ad15e83a 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/ScriptEngine/VictoryConditions.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/ScriptEngine/VictoryConditions.cpp @@ -276,8 +276,13 @@ Bool VictoryConditions::hasAchievedVictory(Player *player) for (Int i = 0; i < MAX_PLAYER_COUNT; ++i) { - if (player == m_players[i] && m_isVictorious[i]) - return true; + if (player == m_players[i]) + { + if (m_isVictorious[i]) + return true; + + break; + } } return false; @@ -294,8 +299,13 @@ Bool VictoryConditions::hasBeenDefeated(Player *player) for (Int i = 0; i < MAX_PLAYER_COUNT; ++i) { - if (player == m_players[i] && m_isDefeated[i]) - return true; + if (player == m_players[i]) + { + if (m_isDefeated[i]) + return true; + + break; + } } return false; diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/ScriptEngine/VictoryConditions.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/ScriptEngine/VictoryConditions.cpp index be083a3df41..2b25fa3c4c1 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/ScriptEngine/VictoryConditions.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/ScriptEngine/VictoryConditions.cpp @@ -278,8 +278,13 @@ Bool VictoryConditions::hasAchievedVictory(Player *player) for (Int i = 0; i < MAX_PLAYER_COUNT; ++i) { - if (player == m_players[i] && m_isVictorious[i]) - return true; + if (player == m_players[i]) + { + if (m_isVictorious[i]) + return true; + + break; + } } return false; @@ -296,8 +301,13 @@ Bool VictoryConditions::hasBeenDefeated(Player *player) for (Int i = 0; i < MAX_PLAYER_COUNT; ++i) { - if (player == m_players[i] && m_isDefeated[i]) - return true; + if (player == m_players[i]) + { + if (m_isDefeated[i]) + return true; + + break; + } } return false; From f378df772ce7c6643d483b54ea81a60bfa5e1652 Mon Sep 17 00:00:00 2001 From: Stubbjax Date: Tue, 17 Feb 2026 11:03:24 +1100 Subject: [PATCH 6/7] refactor: Split some update functionality into functions --- .../ScriptEngine/VictoryConditions.cpp | 46 +++++++++++-------- .../ScriptEngine/VictoryConditions.cpp | 46 +++++++++++-------- 2 files changed, 56 insertions(+), 36 deletions(-) diff --git a/Generals/Code/GameEngine/Source/GameLogic/ScriptEngine/VictoryConditions.cpp b/Generals/Code/GameEngine/Source/GameLogic/ScriptEngine/VictoryConditions.cpp index 744ad15e83a..df59c9f7bbd 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/ScriptEngine/VictoryConditions.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/ScriptEngine/VictoryConditions.cpp @@ -92,6 +92,8 @@ class VictoryConditions : public VictoryConditionsInterface Bool amIObserver( void ) { return m_isObserver;} ///< Am I an observer?( need this for scripts ) virtual UnsignedInt getEndFrame( void ) { return m_endFrame; } ///< on which frame was the game effectively over? private: + Player* findFirstVictoriousPlayer(); ///< Find the first player that has achieved victory. + void markAllianceVictorious(Player* victoriousPlayer); ///< Mark the victorious player and his allies as victorious. Bool multipleAlliancesExist(void); ///< Are there multiple alliances still alive? Player* m_players[MAX_PLAYER_COUNT]; @@ -187,26 +189,10 @@ void VictoryConditions::update( void ) // TheSuperHackers @bugfix Stubbjax 11/02/2026 Cache victory status so that premature exits don't void the victory. - Player* victoriousPlayer = nullptr; - for (Int i = 0; i < MAX_PLAYER_COUNT; ++i) - { - Player* player = m_players[i]; - if (player && !hasSinglePlayerBeenDefeated(player)) - { - victoriousPlayer = player; - break; - } - } + Player* victoriousPlayer = findFirstVictoriousPlayer(); if (victoriousPlayer) - { - for (Int i = 0; i < MAX_PLAYER_COUNT; ++i) - { - Player* player = m_players[i]; - if (player == victoriousPlayer || (player && areAllies(player, victoriousPlayer))) - m_isVictorious[i] = true; - } - } + markAllianceVictorious(victoriousPlayer); } } @@ -265,6 +251,30 @@ void VictoryConditions::update( void ) } } +//------------------------------------------------------------------------------------------------- +Player* VictoryConditions::findFirstVictoriousPlayer() +{ + for (Int i = 0; i < MAX_PLAYER_COUNT; ++i) + { + Player* player = m_players[i]; + if (player && !hasSinglePlayerBeenDefeated(player)) + return player; + } + + return nullptr; +} + +//------------------------------------------------------------------------------------------------- +void VictoryConditions::markAllianceVictorious(Player* victoriousPlayer) +{ + for (Int i = 0; i < MAX_PLAYER_COUNT; ++i) + { + Player* player = m_players[i]; + if (player == victoriousPlayer || (player && areAllies(player, victoriousPlayer))) + m_isVictorious[i] = true; + } +} + //------------------------------------------------------------------------------------------------- Bool VictoryConditions::hasAchievedVictory(Player *player) { diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/ScriptEngine/VictoryConditions.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/ScriptEngine/VictoryConditions.cpp index 2b25fa3c4c1..9f436ff86cc 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/ScriptEngine/VictoryConditions.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/ScriptEngine/VictoryConditions.cpp @@ -93,6 +93,8 @@ class VictoryConditions : public VictoryConditionsInterface Bool amIObserver( void ) { return m_isObserver;} ///< Am I an observer?( need this for scripts ) virtual UnsignedInt getEndFrame( void ) { return m_endFrame; } ///< on which frame was the game effectively over? private: + Player* findFirstVictoriousPlayer(); ///< Find the first player that has achieved victory. + void markAllianceVictorious(Player* victoriousPlayer); ///< Mark the victorious player and his allies as victorious. Bool multipleAlliancesExist(void); ///< Are there multiple alliances still alive? Player* m_players[MAX_PLAYER_COUNT]; @@ -188,26 +190,10 @@ void VictoryConditions::update( void ) // TheSuperHackers @bugfix Stubbjax 11/02/2026 Cache victory status so that premature exits don't void the victory. - Player* victoriousPlayer = nullptr; - for (Int i = 0; i < MAX_PLAYER_COUNT; ++i) - { - Player* player = m_players[i]; - if (player && !hasSinglePlayerBeenDefeated(player)) - { - victoriousPlayer = player; - break; - } - } + Player* victoriousPlayer = findFirstVictoriousPlayer(); if (victoriousPlayer) - { - for (Int i = 0; i < MAX_PLAYER_COUNT; ++i) - { - Player* player = m_players[i]; - if (player == victoriousPlayer || (player && areAllies(player, victoriousPlayer))) - m_isVictorious[i] = true; - } - } + markAllianceVictorious(victoriousPlayer); } } @@ -267,6 +253,30 @@ void VictoryConditions::update( void ) } } +//------------------------------------------------------------------------------------------------- +Player* VictoryConditions::findFirstVictoriousPlayer() +{ + for (Int i = 0; i < MAX_PLAYER_COUNT; ++i) + { + Player* player = m_players[i]; + if (player && !hasSinglePlayerBeenDefeated(player)) + return player; + } + + return nullptr; +} + +//------------------------------------------------------------------------------------------------- +void VictoryConditions::markAllianceVictorious(Player* victoriousPlayer) +{ + for (Int i = 0; i < MAX_PLAYER_COUNT; ++i) + { + Player* player = m_players[i]; + if (player == victoriousPlayer || (player && areAllies(player, victoriousPlayer))) + m_isVictorious[i] = true; + } +} + //------------------------------------------------------------------------------------------------- Bool VictoryConditions::hasAchievedVictory(Player *player) { From e297db3b9f7f363534768db76f279e6255dae9e6 Mon Sep 17 00:00:00 2001 From: Stubbjax Date: Tue, 17 Feb 2026 11:56:32 +1100 Subject: [PATCH 7/7] docs: Add comments --- .../Source/GameLogic/ScriptEngine/VictoryConditions.cpp | 4 ++++ .../Source/GameLogic/ScriptEngine/VictoryConditions.cpp | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/Generals/Code/GameEngine/Source/GameLogic/ScriptEngine/VictoryConditions.cpp b/Generals/Code/GameEngine/Source/GameLogic/ScriptEngine/VictoryConditions.cpp index df59c9f7bbd..bfef969f06f 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/ScriptEngine/VictoryConditions.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/ScriptEngine/VictoryConditions.cpp @@ -267,6 +267,10 @@ Player* VictoryConditions::findFirstVictoriousPlayer() //------------------------------------------------------------------------------------------------- void VictoryConditions::markAllianceVictorious(Player* victoriousPlayer) { + // This marks the player and any allies as victorious, including defeated allies. + // This also ensures players retain their victorious status if their assets are destroyed after + // the victory conditions are met (e.g. when quitting the game prior to the victory screen). + for (Int i = 0; i < MAX_PLAYER_COUNT; ++i) { Player* player = m_players[i]; diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/ScriptEngine/VictoryConditions.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/ScriptEngine/VictoryConditions.cpp index 9f436ff86cc..2f7b27f6902 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/ScriptEngine/VictoryConditions.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/ScriptEngine/VictoryConditions.cpp @@ -269,6 +269,10 @@ Player* VictoryConditions::findFirstVictoriousPlayer() //------------------------------------------------------------------------------------------------- void VictoryConditions::markAllianceVictorious(Player* victoriousPlayer) { + // This marks the player and any allies as victorious, including defeated allies. + // This also ensures players retain their victorious status if their assets are destroyed after + // the victory conditions are met (e.g. when quitting the game prior to the victory screen). + for (Int i = 0; i < MAX_PLAYER_COUNT; ++i) { Player* player = m_players[i];