Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -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->isLocalAlliedDefeat() || !TheVictoryConditions->getEndFrame())
{
++stats.losses[ptIdx];
}
else
{
++stats.wins[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;
Expand All @@ -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++;
Expand All @@ -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;
Expand All @@ -1646,13 +1621,29 @@ winName.format("ScoreScreen.wnd:StaticTextScore%d", pos);
}
else
{
++stats.losses[ptIdx];

stats.lossesInARow++;
stats.desyncsInARow = 0;
stats.disconsInARow = 0;
stats.winsInARow = 0;
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]++;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,15 @@ 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* findFirstUndefeatedPlayer(); ///< Find the first player that has not been defeated.
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];
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;
Expand Down Expand Up @@ -127,6 +132,7 @@ void VictoryConditions::reset( void )
{
m_players[i] = nullptr;
m_isDefeated[i] = false;
m_isVictorious[i] = false;
}
m_localSlotNum = -1;

Expand All @@ -139,42 +145,52 @@ 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; i<MAX_PLAYER_COUNT; ++i)
Player* player = m_players[i];

if (player && !hasSinglePlayerBeenDefeated(player))
{
player = m_players[i];
if (player && !hasSinglePlayerBeenDefeated(player))
if (alive)
{
if (alive)
// check to verify they are on the same team
if (!areAllies(alive, player))
{
// check to verify they are on the same team
if (!areAllies(alive, player))
{
multipleAlliances = true;
break;
}
}
else
{
alive = player; // save this pointer to check against
return true;
}
}
else
{
alive = player; // save this pointer to check against
}
}
}

if (!multipleAlliances)
return false;
}

//-------------------------------------------------------------------------------------------------
void VictoryConditions::update( void )
{
if (!TheRecorder->isMultiplayer() || (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();

Player* victoriousPlayer = findFirstUndefeatedPlayer();

if (victoriousPlayer)
markAllianceVictorious(victoriousPlayer);
}
}

Expand Down Expand Up @@ -233,19 +249,50 @@ void VictoryConditions::update( void )
}
}

//-------------------------------------------------------------------------------------------------
Player* VictoryConditions::findFirstUndefeatedPlayer()
{
for (Int i = 0; i < MAX_PLAYER_COUNT; ++i)
{
Player* player = m_players[i];
if (player && !hasSinglePlayerBeenDefeated(player))
return player;
}

return nullptr;
}

// TheSuperHackers @bugfix Stubbjax 11/02/2026 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).
//-------------------------------------------------------------------------------------------------
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)
{
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<MAX_PLAYER_COUNT; ++i)
if (player == m_players[i])
{
if ( m_players[i] && !hasSinglePlayerBeenDefeated(m_players[i]) &&
(player == m_players[i] || areAllies(m_players[i], player)) )
if (m_isVictorious[i])
return true;

break;
}
}

Expand All @@ -258,8 +305,19 @@ Bool VictoryConditions::hasBeenDefeated(Player *player)
if (!player)
return false;

if (m_singleAllianceRemaining && !hasAchievedVictory(player))
return true;
if (!m_singleAllianceRemaining)
return false;

for (Int i = 0; i < MAX_PLAYER_COUNT; ++i)
{
if (player == m_players[i])
{
if (m_isDefeated[i])
return true;

break;
}
}

return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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->isLocalAlliedDefeat() || !TheVictoryConditions->getEndFrame())
{
++stats.losses[ptIdx];
}
else
{
++stats.wins[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;
Expand All @@ -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++;
Expand All @@ -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;
Expand All @@ -1913,13 +1888,29 @@ winName.format("ScoreScreen.wnd:StaticTextScore%d", pos);
}
else
{
++stats.losses[ptIdx];

stats.lossesInARow++;
stats.desyncsInARow = 0;
stats.disconsInARow = 0;
stats.winsInARow = 0;
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]++;
Expand Down
Loading
Loading