# How to teach AI to not make bad peace offers to some distant Team members?

#### Rompersenor

##### Chieftain
Hello, what I currently hate in the game is this situation:

1) I play multiplayer, in a team of 2 players vs AIs (we are both Team 1, each AI has its own team)
2) Some AI declares war on us.
3) One of us is doing ok, but one of us is crumbling under the attack of this AI.
4) This AI of course has no interest in Peace treaty with the losing player (which is sound)... but makes white peace with the second player (...thus the war ends for the whole team) happily, even if it would get massive gains from continuing the war against the losing player. Clearly it cannot comprehend that it will make peace with the losing player as well.

My question: How does the reasoning for the AI work? What exactly to write into a Proposal to the Congress? They require specific values to change, which I have no clues about. I would propose that if there is some kind of numerical variable telling the AI how much it wants to continue war against this player, then make an average of this value over all players in the same team - ultimately the AI will have the same approach to all team members and will require the same Peace Treaty deal from them.

There is no special logic for teams re: making peace. If any AI member of the team is willing to make peace, you can make peace with the entire team that way. Likewise, if the AI is willing to make peace with one player, they will make peace with all players on a team.

One exception: the AI will never make peace (or capitulate) if it's on the same team as a human player. The human makes all the decisions. They also won't agree to accept Embassies or trade Open Borders, Defensive Pacts or Research Agreements.

There is a lot of code here, so I can't reproduce all of it, but feel free to ask any more specific questions.

Function to determine peace willingness:
Code:
``````void CvDiplomacyAI::DoUpdatePeaceTreatyWillingness(bool bMyTurn)
{
CvCity* pCapital = GetPlayer()->getCapitalCity();
bool bLog = bMyTurn && GC.getLogging() && GC.getAILogging(); // Only log this once per turn to prevent log spam
int iLoop = 0;

if (GC.getGame().isOption(GAMEOPTION_ALWAYS_WAR) || GC.getGame().isOption(GAMEOPTION_NO_CHANGING_WAR_PEACE) || GetPlayer()->isHuman() || GetPlayer()->IsAITeammateOfHuman() || GetPlayer()->IsVassalOfSomeone())
{
for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++)
{
PlayerTypes ePlayer = (PlayerTypes) iPlayerLoop;
SetTreatyWillingToOffer(ePlayer, NO_PEACE_TREATY_TYPE);
SetTreatyWillingToAccept(ePlayer, NO_PEACE_TREATY_TYPE);
}
if (bLog && !GetPlayer()->isHuman())
{
CvString strOutBuf;
CvString strBaseString;
CvString playerName;
CvString strLogName;

// Find the name of this civ and city
playerName = m_pPlayer->getCivilizationShortDescription();

// Open the log file
if (GC.getPlayerAndCityAILogSplit())
{
strLogName = "DiplomacyAI_Peace_Log" + playerName + ".csv";
}
else
{
strLogName = "DiplomacyAI_Peace_Log.csv";
}

FILogFile* pLog = NULL;
pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp);

// Get the leading info for this line
strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns());
strBaseString += playerName + " VS. Everyone";

strOutBuf.Format("Not allowed to change war/peace status with anyone!");

strBaseString += strOutBuf;
pLog->Msg(strBaseString);
}
return;
}

// STEP 1: See if we're in a critical state and check which players are valid
bool bCriticalState = GetPlayer()->IsEmpireSuperUnhappy() || !pCapital || pCapital->isInDangerOfFalling(true) || pCapital->getDamage() >= (pCapital->GetMaxHitPoints()/2);
bool bMakePeaceWithAllMinors = false;
bool bWorldConquest = IsGoingForWorldConquest() || IsCloseToWorldConquest();
bool bDiplomatic = IsGoingForDiploVictory() || IsDiplomat() || GetPlayer()->GetPlayerTraits()->IsDiplomat();
vector<PlayerTypes> vValidPlayers;

for (int iPlayerLoop = 0; iPlayerLoop < MAX_CIV_PLAYERS; iPlayerLoop++)
{
PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop;

if (GET_PLAYER(eLoopPlayer).isAlive() && IsAtWar(eLoopPlayer))
{
if (GET_PLAYER(eLoopPlayer).isMajorCiv())
{
if (IsPeaceBlocked(eLoopPlayer))
{
SetTreatyWillingToOffer(eLoopPlayer, NO_PEACE_TREATY_TYPE);
SetTreatyWillingToAccept(eLoopPlayer, NO_PEACE_TREATY_TYPE);

if (bLog)
{
CvString strOutBuf;
CvString strBaseString;
CvString playerName;
CvString otherPlayerName;
CvString strLogName;
CvString strPeaceBlockReason;

FmtPeaceBlockReasonLogStr(strPeaceBlockReason, GetID(), eLoopPlayer, GetPeaceBlockReason(eLoopPlayer));

// Find the name of this civ and city
playerName = m_pPlayer->getCivilizationShortDescription();

// Open the log file
if (GC.getPlayerAndCityAILogSplit())
{
strLogName = "DiplomacyAI_Peace_Log" + playerName + ".csv";
}
else
{
strLogName = "DiplomacyAI_Peace_Log.csv";
}

FILogFile* pLog = NULL;
pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp);

// Get the leading info for this line
strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns());
otherPlayerName = GET_PLAYER(eLoopPlayer).getCivilizationShortDescription();
strBaseString += playerName + " VS. " + otherPlayerName;

strOutBuf.Format("PEACE BLOCKED! ");
strOutBuf += strPeaceBlockReason;

strBaseString += strOutBuf;
pLog->Msg(strBaseString);
}
}
else
{
vValidPlayers.push_back(eLoopPlayer);
}
}

if (!bCriticalState)
{
WarStateTypes eWarState = GetWarState(eLoopPlayer);

if (eWarState == WAR_STATE_NEARLY_DEFEATED)
{
bCriticalState = true;
}
else if (eWarState <= WAR_STATE_TROUBLED)
{
if (GetPlayer()->IsNoNewWars() || !GET_PLAYER(eLoopPlayer).isMajorCiv() || !IsPhonyWar(eLoopPlayer) || GetWarScore(eLoopPlayer) <= WARSCORE_THRESHOLD_NEGATIVE)
bMakePeaceWithAllMinors = true;
}
}
}
else if (GET_PLAYER(eLoopPlayer).isMajorCiv())
{
SetTreatyWillingToOffer(eLoopPlayer, NO_PEACE_TREATY_TYPE);
SetTreatyWillingToAccept(eLoopPlayer, NO_PEACE_TREATY_TYPE);
}
}
if (bCriticalState)
bMakePeaceWithAllMinors = true;

// STEP 2: Make peace with any City-States here - this happens instantly!
if (bMyTurn)
{
for (int iPlayerLoop = MAX_MAJOR_CIVS; iPlayerLoop < MAX_CIV_PLAYERS; iPlayerLoop++)
{
PlayerTypes eMinor = (PlayerTypes) iPlayerLoop;

if (IsAtWar(eMinor))
{
if (IsPeaceBlocked(eMinor))
{
if (bLog)
{
CvString strOutBuf;
CvString strBaseString;
CvString playerName;
CvString otherPlayerName;
CvString strLogName;
CvString strPeaceBlockReason;

FmtPeaceBlockReasonLogStr(strPeaceBlockReason, GetID(), eMinor, GetPeaceBlockReason(eMinor));

// Find the name of this civ and city
playerName = m_pPlayer->getCivilizationShortDescription();

// Open the log file
if (GC.getPlayerAndCityAILogSplit())
{
strLogName = "DiplomacyAI_Peace_Log" + playerName + ".csv";
}
else
{
strLogName = "DiplomacyAI_Peace_Log.csv";
}

FILogFile* pLog = NULL;
pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp);

// Get the leading info for this line
strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns());
otherPlayerName = GET_PLAYER(eMinor).getCivilizationShortDescription();
strBaseString += playerName + " VS. " + otherPlayerName;

strOutBuf.Format("PEACE BLOCKED! ");
strOutBuf += strPeaceBlockReason;

strBaseString += strOutBuf;
pLog->Msg(strBaseString);
}
}
else
{
if (bMakePeaceWithAllMinors || GetCSWarTargetPlayer() != eMinor)
{
if (!bCriticalState)
{
// Exception if making peace with this City-State would prevent us from achieving Domination Victory
if (GC.getGame().WouldMakingPeacePreventDominationVictory(GetID(), eMinor))
{
if (IsGoingForWorldConquest() || IsCloseToWorldConquest())
continue;
}

// Exception under two circumstances: the minor's capital or a city they captured from us are in danger from us
for (CvCity* pLoopCity = GET_PLAYER(eMinor).firstCity(&iLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER(eMinor).nextCity(&iLoop))
{
if (pLoopCity->isCapital() || GET_PLAYER(pLoopCity->getOriginalOwner()).getTeam() == GetTeam())
{
if (pLoopCity->IsInDanger(GetID()) && (pLoopCity->isUnderSiege() || pLoopCity->IsBlockadedWaterAndLand()))
{
continue;
}
}
}
}

GET_TEAM(GetTeam()).makePeace(GET_PLAYER(eMinor).getTeam(), true, false, GetID());

if (bLog)
{
CvString strOutBuf;
CvString strBaseString;
CvString playerName;
CvString otherPlayerName;
CvString strLogName;

// Find the name of this civ and city
playerName = m_pPlayer->getCivilizationShortDescription();

// Open the log file
if (GC.getPlayerAndCityAILogSplit())
{
strLogName = "DiplomacyAI_Peace_Log" + playerName + ".csv";
}
else
{
strLogName = "DiplomacyAI_Peace_Log.csv";
}

FILogFile* pLog = NULL;
pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp);

// Get the leading info for this line
strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns());
otherPlayerName = GET_PLAYER(eMinor).getCivilizationShortDescription();
strBaseString += playerName + " VS. " + otherPlayerName;

if (bCriticalState)
strOutBuf.Format("Making peace with all possible City-States because we're in a critical state!");
else if (bMakePeaceWithAllMinors)
strOutBuf.Format("Making peace with all possible City-States because we're doing poorly in war!");
else
strOutBuf.Format("This City-State is no longer our CS war target!");

strBaseString += strOutBuf;
pLog->Msg(strBaseString);
}
}
}
}
}
}

// STEP 3: Determine whether we're willing to make peace with any majors
vector<PlayerTypes> vMakePeacePlayers;
bool bInTerribleShape = false;
bool bUABonusesFromCityConquest = false;
if (!bCriticalState)
{
bInTerribleShape = GetPlayer()->IsInTerribleShapeForWar();
CvPlayerTraits* pTraits = GetPlayer()->GetPlayerTraits();
if (!bInTerribleShape)
{
// Assyria, Japan, Indonesia, The Aztecs, Nuclear Gandhi, and anyone who keeps conquered buildings will prolong wars longer if they are winning and the enemy's cities are in danger from them.
bUABonusesFromCityConquest = pTraits->IsTechFromCityConquer();
bUABonusesFromCityConquest |= pTraits->GetCityConquestGWAM() > 0;
bUABonusesFromCityConquest |= pTraits->GetUniqueLuxuryQuantity() > 0;
bUABonusesFromCityConquest |= pTraits->GetGoldenAgeFromVictory() > 0;
bUABonusesFromCityConquest |= pTraits->IsKeepConqueredBuildings() || GetPlayer()->IsKeepConqueredBuildings();
bUABonusesFromCityConquest |= pTraits->GetCultureBonusModifierConquest() > 0;
bUABonusesFromCityConquest |= pTraits->GetProductionBonusModifierConquest() > 0;
bUABonusesFromCityConquest |= pTraits->IsFreeGreatWorkOnConquest();
bUABonusesFromCityConquest |= pTraits->IsConquestOfTheWorld();
bUABonusesFromCityConquest |= IsNuclearGandhi();
}
}

for (std::vector<PlayerTypes>::iterator it = vValidPlayers.begin(); it != vValidPlayers.end(); it++)
{
if (bCriticalState)
{
vMakePeacePlayers.push_back(*it);
if (bLog)
{
CvString strOutBuf;
CvString strBaseString;
CvString playerName;
CvString otherPlayerName;
CvString strLogName;

// Find the name of this civ and city
playerName = m_pPlayer->getCivilizationShortDescription();

// Open the log file
if (GC.getPlayerAndCityAILogSplit())
{
strLogName = "DiplomacyAI_Peace_Log" + playerName + ".csv";
}
else
{
strLogName = "DiplomacyAI_Peace_Log.csv";
}

FILogFile* pLog = NULL;
pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp);

// Get the leading info for this line
strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns());
otherPlayerName = GET_PLAYER(*it).getCivilizationShortDescription();
strBaseString += playerName + " VS. " + otherPlayerName;

strOutBuf.Format("Making peace with all possible civilizations because we're in a critical state!");

strBaseString += strOutBuf;
pLog->Msg(strBaseString);
}
continue;
}

// Let's see how much danger each of us are currently in ...
int iWarDuration = min(GetPlayer()->GetPlayerNumTurnsAtWar(*it), GetPlayer()->GetPlayerNumTurnsSinceCityCapture(*it));
int iWarScore = GetWarScore(*it);
int iOurDanger = 0;
int iTheirDanger = 0;
bool bSeriousDangerUs = false;
bool bSeriousDangerThem = false;
ReligionTypes eMyReligion = GetPlayer()->GetReligions()->GetOwnedReligion();
ReligionTypes eTheirReligion = GET_PLAYER(*it).GetReligions()->GetOwnedReligion();
vector<PlayerTypes> vOurWarAllies = GetOffensiveWarAllies(*it, /*bIncludeMinors*/ true, /*bReverseMode*/ false);
vector<PlayerTypes> vTheirWarAllies = GetOffensiveWarAllies(*it, /*bIncludeMinors*/ true, /*bReverseMode*/ true);
vOurWarAllies.push_back(GetID());
vTheirWarAllies.push_back(*it);
vector<PlayerTypes> vMyTeam = GET_TEAM(GetTeam()).getPlayers();
vector<int> vEnemyCitiesEndangered;
vector<int> vEnemyCitiesEndangeredByUs;

for (CvCity* pLoopCity = m_pPlayer->firstCity(&iLoop); pLoopCity != NULL; pLoopCity = m_pPlayer->nextCity(&iLoop))
{
if (pLoopCity->IsInDangerFromPlayers(vTheirWarAllies)) // Only care if we're in danger from them!
{
int iDangerMod = 1;

//look at the tactical map (is it up to date?)
CvTacticalDominanceZone* pLandZone = m_pPlayer->GetTacticalAI()->GetTacticalAnalysisMap()->GetZoneByCity(pLoopCity,false);
CvTacticalDominanceZone* pWaterZone = m_pPlayer->GetTacticalAI()->GetTacticalAnalysisMap()->GetZoneByCity(pLoopCity,true);
if (pLandZone && pLandZone->GetOverallDominanceFlag()==TACTICAL_DOMINANCE_ENEMY)
iDangerMod++;
if (pWaterZone && pWaterZone->GetOverallDominanceFlag()==TACTICAL_DOMINANCE_ENEMY)
iDangerMod++;

if (pLoopCity->isUnderSiege())
{
if (pLoopCity->isInDangerOfFalling(true))
iDangerMod += 3;
else
iDangerMod++;

if (pLoopCity->isInDangerOfFalling(true) || pLoopCity->getDamage() >= (pLoopCity->GetMaxHitPoints()/2))
{
if (pLoopCity->getOriginalOwner() == GetID())
{
bSeriousDangerUs = true;
}
else if (pLoopCity->isCapital() || pLoopCity->IsOriginalMajorCapital() || (eMyReligion != NO_RELIGION && pLoopCity->GetCityReligions()->IsHolyCityForReligion(eMyReligion)) || (eTheirReligion != NO_RELIGION && pLoopCity->GetCityReligions()->IsHolyCityForReligion(eTheirReligion))
|| pLoopCity->HasAnyWonder() || pLoopCity->getNumNationalWonders() > 0)
{
bSeriousDangerUs = true;
}
}
}

if (iDangerMod > 1 && pLoopCity->IsInDanger(*it) && GetPlayer()->GetMilitaryAI()->IsExposedToEnemy(pLoopCity, *it))
{
iDangerMod++;
}

if (pLoopCity->isCapital())
iDangerMod *= 3;
else if (pLoopCity->IsOriginalMajorCapital() || (eMyReligion != NO_RELIGION && pLoopCity->GetCityReligions()->IsHolyCityForReligion(eMyReligion)) || (eTheirReligion != NO_RELIGION && pLoopCity->GetCityReligions()->IsHolyCityForReligion(eTheirReligion)) || pLoopCity->HasAnyWonder())
iDangerMod *= 2;
else if (pLoopCity->GetCityReligions()->IsHolyCityAnyReligion() || pLoopCity->getNumNationalWonders() > 0)
iDangerMod++;

iOurDanger += iDangerMod;
}
}

for (CvCity* pLoopCity = GET_PLAYER(*it).firstCity(&iLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER(*it).nextCity(&iLoop))
{
if (pLoopCity->IsInDangerFromPlayers(vOurWarAllies)) // Only care if they're in danger from us!
{
int iDangerMod = 1;

//look at the tactical map (is it up to date?)
CvTacticalDominanceZone* pLandZone = GET_PLAYER(*it).GetTacticalAI()->GetTacticalAnalysisMap()->GetZoneByCity(pLoopCity,false);
CvTacticalDominanceZone* pWaterZone = GET_PLAYER(*it).GetTacticalAI()->GetTacticalAnalysisMap()->GetZoneByCity(pLoopCity,true);

if (pLandZone && pLandZone->GetOverallDominanceFlag()==TACTICAL_DOMINANCE_ENEMY)
iDangerMod++;
if (pWaterZone && pWaterZone->GetOverallDominanceFlag()==TACTICAL_DOMINANCE_ENEMY)
iDangerMod++;

if (pLoopCity->isUnderSiege())
{
if (pLoopCity->isInDangerOfFalling(true))
iDangerMod += 3;
else
iDangerMod++;

if (pLoopCity->isInDangerOfFalling(true) || pLoopCity->getDamage() >= (pLoopCity->GetMaxHitPoints()/2))
{
bSeriousDangerThem = true;
}
}

if (iDangerMod > 1)
{
vEnemyCitiesEndangered.push_back(pLoopCity->GetID());

if (pLoopCity->IsInDangerFromPlayers(vMyTeam))
{
vEnemyCitiesEndangeredByUs.push_back(pLoopCity->GetID());

if (pLoopCity->IsInDanger(GetID()) && GetPlayer()->GetMilitaryAI()->IsPreferredAttackTarget(pLoopCity))
{
iDangerMod++;
}
}
}

if (pLoopCity->isCapital())
iDangerMod *= 3;
else if (pLoopCity->IsOriginalMajorCapital() || (eMyReligion != NO_RELIGION && pLoopCity->GetCityReligions()->IsHolyCityForReligion(eMyReligion)) || (eTheirReligion != NO_RELIGION && pLoopCity->GetCityReligions()->IsHolyCityForReligion(eTheirReligion)) || pLoopCity->HasAnyWonder())
iDangerMod *= 2;
else if (pLoopCity->GetCityReligions()->IsHolyCityAnyReligion())
iDangerMod++;

iTheirDanger += iDangerMod;
}
}

bool bCapturedKeyCity = IsCapitalCapturedBy(*it, true, true) || IsHolyCityCapturedBy(*it, true, true);
bool bPhonyWar = IsPhonyWar(*it);
bool bCapturedAnyCityFromUs = bCapturedKeyCity;
bool bCapturedAnyCityWeWantToLiberate = false;
bool bReadyForVassalage = iWarScore >= 75 && !bCapturedKeyCity && !IsEndgameAggressiveTo(*it) && GET_TEAM(GET_PLAYER(*it).getTeam()).canBecomeVassal(GetTeam()) && !IsUntrustworthy(*it);

if (!bInTerribleShape)
{
// Let's check if they have any of our cities. If they do, no vassalage!
for (CvCity* pLoopCity = GET_PLAYER(*it).firstCity(&iLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER(*it).nextCity(&iLoop))
{
PlayerTypes eOriginalOwner = pLoopCity->getOriginalOwner();

if (GET_PLAYER(eOriginalOwner).getTeam() == GET_PLAYER(*it).getTeam())
continue;

CvPlot* pCityPlot = pLoopCity->plot();
if (!pCityPlot)
continue;

bool bEndangeredPeriod = std::find(vEnemyCitiesEndangered.begin(), vEnemyCitiesEndangered.end(), pLoopCity->GetID()) != vEnemyCitiesEndangered.end();
bool bEndangeredByOurTeam = bEndangeredPeriod && std::find(vEnemyCitiesEndangeredByUs.begin(), vEnemyCitiesEndangeredByUs.end(), pLoopCity->GetID()) != vEnemyCitiesEndangeredByUs.end();

// Ignore cities that are too far away from us (unless endangered).
if (GetPlayer()->GetCityDistanceInPlots(pCityPlot) > 12)
{
if (!bEndangeredByOurTeam && (!bEndangeredPeriod || GET_PLAYER(eOriginalOwner).getTeam() != GetTeam()))
continue;
}

if (GET_PLAYER(eOriginalOwner).getTeam() == GetTeam())
{
bCapturedAnyCityFromUs = true;
}
// Is this a city that we want to liberate?
else if (IsTryingToLiberate(pLoopCity))
{
bCapturedAnyCityWeWantToLiberate = true;
}

if (bCapturedAnyCityFromUs && bCapturedAnyCityWeWantToLiberate)
break;

// Is this a valuable city endangered by our team?
// Alternatively, if we're nasty/hateful or get bonuses from conquering cities, we're rarely willing to vassalize if ANY city is vulnerable.
{
bool bProceed = bUABonusesFromCityConquest || IsBackstabber() || GetMeanness() >= 8 || GetDiploBalance() <= 2 || GetCivOpinion(*it) == CIV_OPINION_UNFORGIVABLE;
bProceed |= pLoopCity->IsOriginalMajorCapital() || pLoopCity->GetCityReligions()->IsHolyCityAnyReligion() || pLoopCity->HasAnyWonder();

if (bProceed)
{
if (GetPlayer()->GetMilitaryAI()->IsPreferredAttackTarget(pLoopCity))
{
}
else if (pLoopCity->isUnderSiege())
{
}
else
{
//look at the tactical map (is it up to date?)
CvTacticalDominanceZone* pLandZone = GET_PLAYER(*it).GetTacticalAI()->GetTacticalAnalysisMap()->GetZoneByCity(pLoopCity,false);
CvTacticalDominanceZone* pWaterZone = GET_PLAYER(*it).GetTacticalAI()->GetTacticalAnalysisMap()->GetZoneByCity(pLoopCity,true);

if (pLandZone && pLandZone->GetOverallDominanceFlag()==TACTICAL_DOMINANCE_ENEMY)
{
}
else if (pWaterZone && pWaterZone->GetOverallDominanceFlag()==TACTICAL_DOMINANCE_ENEMY)
{
}
}
}
}
}
}

// No peace if they're in danger of losing a city and we're not.
if (bSeriousDangerThem && !bSeriousDangerUs)
{
if (iWarDuration < 40) // Failsafe in case of a problem somewhere - limit the amount of time peace is impossible for
{
SetTreatyWillingToOffer(*it, NO_PEACE_TREATY_TYPE);
SetTreatyWillingToAccept(*it, NO_PEACE_TREATY_TYPE);
if (bLog)
{
CvString strOutBuf;
CvString strBaseString;
CvString playerName;
CvString otherPlayerName;
CvString strLogName;

// Find the name of this civ and city
playerName = m_pPlayer->getCivilizationShortDescription();

// Open the log file
if (GC.getPlayerAndCityAILogSplit())
{
strLogName = "DiplomacyAI_Peace_Log" + playerName + ".csv";
}
else
{
strLogName = "DiplomacyAI_Peace_Log.csv";
}

FILogFile* pLog = NULL;
pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp);

// Get the leading info for this line
strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns());
otherPlayerName = GET_PLAYER(*it).getCivilizationShortDescription();
strBaseString += playerName + " VS. " + otherPlayerName;

strOutBuf.Format("No peace! They're in serious danger of losing a city and we're not!");

strBaseString += strOutBuf;
pLog->Msg(strBaseString);
}
continue;
}
}
// If we're in serious danger, let's offer peace.
else if ((iWarScore <= -90 && iOurDanger > 0) || (iWarScore <= -50 && (bInTerribleShape || bSeriousDangerUs)))
{
vMakePeacePlayers.push_back(*it);
if (bLog)
{
CvString strOutBuf;
CvString strBaseString;
CvString playerName;
CvString otherPlayerName;
CvString strLogName;

// Find the name of this civ and city
playerName = m_pPlayer->getCivilizationShortDescription();

// Open the log file
if (GC.getPlayerAndCityAILogSplit())
{
strLogName = "DiplomacyAI_Peace_Log" + playerName + ".csv";
}
else
{
strLogName = "DiplomacyAI_Peace_Log.csv";
}

FILogFile* pLog = NULL;
pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp);

// Get the leading info for this line
strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns());
otherPlayerName = GET_PLAYER(*it).getCivilizationShortDescription();
strBaseString += playerName + " VS. " + otherPlayerName;

strOutBuf.Format("Automatic peace offer: We're badly losing this war!");

strBaseString += strOutBuf;
pLog->Msg(strBaseString);
}
continue;
}
// Let's offer peace if we're in danger of losing a city we originally founded (or a really important city) and they're not at risk of losing a city
else if (bSeriousDangerUs && !bSeriousDangerThem)
{
vMakePeacePlayers.push_back(*it);
if (bLog)
{
CvString strOutBuf;
CvString strBaseString;
CvString playerName;
CvString otherPlayerName;
CvString strLogName;

// Find the name of this civ and city
playerName = m_pPlayer->getCivilizationShortDescription();

// Open the log file
if (GC.getPlayerAndCityAILogSplit())
{
strLogName = "DiplomacyAI_Peace_Log" + playerName + ".csv";
}
else
{
strLogName = "DiplomacyAI_Peace_Log.csv";
}

FILogFile* pLog = NULL;
pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp);

// Get the leading info for this line
strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns());
otherPlayerName = GET_PLAYER(*it).getCivilizationShortDescription();
strBaseString += playerName + " VS. " + otherPlayerName;

strOutBuf.Format("Automatic peace offer: We're in serious danger of losing an important city and they're not in serious danger!");

strBaseString += strOutBuf;
pLog->Msg(strBaseString);
}
continue;
}

// Do not make peace if it would block us from achieving a Domination Victory we're going for (possible with DiploAIOptions.sql)
{
if (IsGoingForWorldConquest() || IsCloseToWorldConquest())
{
SetTreatyWillingToOffer(*it, NO_PEACE_TREATY_TYPE);
SetTreatyWillingToAccept(*it, NO_PEACE_TREATY_TYPE);
if (bLog)
{
CvString strOutBuf;
CvString strBaseString;
CvString playerName;
CvString otherPlayerName;
CvString strLogName;

// Find the name of this civ and city
playerName = m_pPlayer->getCivilizationShortDescription();

// Open the log file
if (GC.getPlayerAndCityAILogSplit())
{
strLogName = "DiplomacyAI_Peace_Log" + playerName + ".csv";
}
else
{
strLogName = "DiplomacyAI_Peace_Log.csv";
}

FILogFile* pLog = NULL;
pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp);

// Get the leading info for this line
strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns());
otherPlayerName = GET_PLAYER(*it).getCivilizationShortDescription();
strBaseString += playerName + " VS. " + otherPlayerName;

strOutBuf.Format("No peace! We'll lose our shot at Domination Victory if we make peace!");

strBaseString += strOutBuf;
pLog->Msg(strBaseString);
}
continue;
}
}

// If this is a phony war and they're not in danger, let's offer peace.
if (bPhonyWar && iTheirDanger == 0)
{
vMakePeacePlayers.push_back(*it);
if (bLog)
{
CvString strOutBuf;
CvString strBaseString;
CvString playerName;
CvString otherPlayerName;
CvString strLogName;

// Find the name of this civ and city
playerName = m_pPlayer->getCivilizationShortDescription();

// Open the log file
if (GC.getPlayerAndCityAILogSplit())
{
strLogName = "DiplomacyAI_Peace_Log" + playerName + ".csv";
}
else
{
strLogName = "DiplomacyAI_Peace_Log.csv";
}

FILogFile* pLog = NULL;
pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp);

// Get the leading info for this line
strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns());
otherPlayerName = GET_PLAYER(*it).getCivilizationShortDescription();
strBaseString += playerName + " VS. " + otherPlayerName;

strOutBuf.Format("Automatic peace offer: This is a phony war and none of their cities are in danger!");

strBaseString += strOutBuf;
pLog->Msg(strBaseString);
}
continue;
}

// Test war progress score: if sufficiently negative, AI will agree to make white peace (at minimum)
// But ignore this if opponent's cities are endangered, unless the war has gone on for too long or we're in serious danger
if (vEnemyCitiesEndangered.size() == 0 || bSeriousDangerUs || bInTerribleShape || iWarDuration >= 40)
{
int iWarProgress = GetWarProgressScore(*it);

if (MOD_BALANCE_VP)
{
iWarProgress -= GetPlayer()->GetUnhappinessFromWarWeariness();

int iHappiness = GetPlayer()->GetExcessHappiness();
if (iHappiness < 50)
{
iWarProgress += (50 - iHappiness) * /*-2*/ GD_INT_GET(WAR_PROGRESS_PER_UNHAPPY);
}
}
else if (GetPlayer()->GetExcessHappiness() < 0)
iWarProgress -= GetPlayer()->GetExcessHappiness() * /*-4*/ GD_INT_GET(WAR_PROGRESS_PER_UNHAPPY);

// Adjust for strategic resource shortages
int iStrategicDeficit = 0;
int iMaxStrategicDeficit = -50;

for (int iResourceLoop = 0; iResourceLoop < GC.getNumResourceInfos(); iResourceLoop++)
{
ResourceTypes eResource = (ResourceTypes) iResourceLoop;
if (eResource == NO_RESOURCE)
continue;

const CvResourceInfo* pkResourceInfo = GC.getResourceInfo(eResource);
if (pkResourceInfo == NULL)
continue;

if (pkResourceInfo->getResourceUsage() != RESOURCEUSAGE_STRATEGIC)
continue;

if (GetPlayer()->getResourceShortageValue(eResource) > 0)
iStrategicDeficit += GetPlayer()->getResourceShortageValue(eResource) * /*-5*/ GD_INT_GET(WAR_PROGRESS_PER_STRATEGIC_DEFICIT);

if (iStrategicDeficit <= iMaxStrategicDeficit)
break;
}

iWarProgress += max(iStrategicDeficit, iMaxStrategicDeficit);

if (iWarProgress <= 0)
{
vMakePeacePlayers.push_back(*it);
if (bLog)
{
CvString strOutBuf;
CvString strBaseString;
CvString playerName;
CvString otherPlayerName;
CvString strLogName;

// Find the name of this civ and city
playerName = m_pPlayer->getCivilizationShortDescription();

// Open the log file
if (GC.getPlayerAndCityAILogSplit())
{
strLogName = "DiplomacyAI_Peace_Log" + playerName + ".csv";
}
else
{
strLogName = "DiplomacyAI_Peace_Log.csv";
}

FILogFile* pLog = NULL;
pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp);

// Get the leading info for this line
strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns());
otherPlayerName = GET_PLAYER(*it).getCivilizationShortDescription();
strBaseString += playerName + " VS. " + otherPlayerName;

strOutBuf.Format("Automatic peace offer: We're making no progress in this war!");

strBaseString += strOutBuf;
pLog->Msg(strBaseString);
}
continue;
}
}

// If not ready for vassalage with a neighbor and the war is nearly won, let's persevere.
if (!bReadyForVassalage && GetWarState(*it) == WAR_STATE_NEARLY_WON && GetPlayer()->GetProximityToPlayer(*it) >= PLAYER_PROXIMITY_CLOSE && iWarDuration < 30 && !bInTerribleShape)
{
SetTreatyWillingToOffer(*it, NO_PEACE_TREATY_TYPE);
SetTreatyWillingToAccept(*it, NO_PEACE_TREATY_TYPE);
if (bLog)
{
CvString strOutBuf;
CvString strBaseString;
CvString playerName;
CvString otherPlayerName;
CvString strLogName;
CvString strNoVassalReason;

// Find the name of this civ and city
playerName = m_pPlayer->getCivilizationShortDescription();

// Open the log file
if (GC.getPlayerAndCityAILogSplit())
{
strLogName = "DiplomacyAI_Peace_Log" + playerName + ".csv";
}
else
{
strLogName = "DiplomacyAI_Peace_Log.csv";
}

FILogFile* pLog = NULL;
pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp);

// Get the leading info for this line
strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns());
otherPlayerName = GET_PLAYER(*it).getCivilizationShortDescription();
strBaseString += playerName + " VS. " + otherPlayerName;

strOutBuf.Format("No peace! We've almost won this war and ");

if (!GET_TEAM(GET_PLAYER(*it).getTeam()).canBecomeVassal(GetTeam()))
strNoVassalReason.Format("we're not able to make them a vassal!");
else if (bCapturedAnyCityFromUs)
strNoVassalReason.Format("they have a nearby city they captured from us!");
else if (IsEndgameAggressiveTo(*it))
strNoVassalReason.Format("they're in danger of winning the game!");
else if (bCapturedAnyCityWeWantToLiberate)
strNoVassalReason.Format("they have a nearby city we want to liberate!");
else if (iWarScore < 75)
strNoVassalReason.Format("war score is less than 75!");
else if (IsUntrustworthy(*it))
strNoVassalReason.Format("we don't trust them to be an obedient vassal!");
else
strNoVassalReason.Format("we still want to capture cities from them!");

strOutBuf += strNoVassalReason;
strBaseString += strOutBuf;
pLog->Msg(strBaseString);
}
continue;
}

bool bWarWeary = false;
if (MOD_BALANCE_VP)
{
int iPercentOfPop = GetPlayer()->GetUnhappinessFromWarWeariness() * 100 / max(1, GetPlayer()->getTotalPopulation());
if (iPercentOfPop >= 20)
bWarWeary = true;
}

// Okay, so no automatic peace. But should we consider peace at all?
bool bConsiderPeace = bReadyForVassalage || bPhonyWar || bSeriousDangerUs || bInTerribleShape || bWarWeary;
bConsiderPeace |= GetCivApproach(*it) > CIV_APPROACH_HOSTILE;
bConsiderPeace |= GetWarState(*it) <= WAR_STATE_TROUBLED;
bConsiderPeace |= iWarDuration >= 30;

// Not worth making peace with. Proceed.
if (!bConsiderPeace)
{
SetTreatyWillingToOffer(*it, NO_PEACE_TREATY_TYPE);
SetTreatyWillingToAccept(*it, NO_PEACE_TREATY_TYPE);
if (bLog)
{
CvString strOutBuf;
CvString strBaseString;
CvString playerName;
CvString otherPlayerName;
CvString strLogName;

// Find the name of this civ and city
playerName = m_pPlayer->getCivilizationShortDescription();

// Open the log file
if (GC.getPlayerAndCityAILogSplit())
{
strLogName = "DiplomacyAI_Peace_Log" + playerName + ".csv";
}
else
{
strLogName = "DiplomacyAI_Peace_Log.csv";
}

FILogFile* pLog = NULL;
pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp);

// Get the leading info for this line
strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns());
otherPlayerName = GET_PLAYER(*it).getCivilizationShortDescription();
strBaseString += playerName + " VS. " + otherPlayerName;

strOutBuf.Format("No peace! We don't have a good enough reason to consider peace yet!");

strBaseString += strOutBuf;
pLog->Msg(strBaseString);
}
continue;
}

// If we've gotten this far, we need to at least consider the merits of peace.
int iPeaceScore = 0;

// War score is of course a significant factor
// If we get a tourism bonus from high warscore or special conquest bonuses, let's not end early!
bool bProlong = GetPlayer()->GetPositiveWarScoreTourismMod() > 0 && GET_PLAYER(GetHighestWarscorePlayer()).getTeam() == GET_PLAYER(*it).getTeam();

if (iWarScore <= 0)
{
iPeaceScore += iWarScore / -2;
bProlong = false;
}
else if (bInTerribleShape || bSeriousDangerUs)
{
iPeaceScore += iWarScore / 3;
bProlong = false;
}
else if (vEnemyCitiesEndangeredByUs.size() > 0)
{
bProlong |= bUABonusesFromCityConquest;
if (!bProlong)
iPeaceScore += iWarScore / 5;
}
else if (!bProlong)
{
iPeaceScore += iWarScore / 5;
}

int iMinimumWarDuration = max(0, /*10*/ GD_INT_GET(WAR_MAJOR_MINIMUM_TURNS));
int iTooLongWarThreshold = max(15, iMinimumWarDuration);
if (bProlong)
{
iTooLongWarThreshold *= 2;
iMinimumWarDuration *= 2;
}

int iDurationPenalty = iWarDuration - iMinimumWarDuration;

// Lack of progress in war increases desire for peace (moreso if far away).
if (iWarDuration > iTooLongWarThreshold)
{
switch (GetPlayer()->GetProximityToPlayer(*it))
{
case PLAYER_PROXIMITY_NEIGHBORS:
iPeaceScore += iDurationPenalty;
break;
case PLAYER_PROXIMITY_CLOSE:
iPeaceScore += (iDurationPenalty * 150) / 100;
break;
case PLAYER_PROXIMITY_FAR:
iPeaceScore += iDurationPenalty * 2;
break;
case NO_PLAYER_PROXIMITY:
case PLAYER_PROXIMITY_DISTANT:
iPeaceScore += iDurationPenalty * 3;
break;
}
}

// War weariness
if (MOD_BALANCE_VP)
{
int iWarWeariness = GetPlayer()->GetUnhappinessFromWarWeariness();
iPeaceScore += iWarWeariness / 4;

if (iWarWeariness > 0 && (bWarWeary || GetPlayer()->IsEmpireUnhappy()))
{
if (GetPlayer()->IsEmpireVeryUnhappy() || (GetPlayer()->IsEmpireUnhappy() && bWarWeary))
iPeaceScore += iWarWeariness / 2;
else
iPeaceScore += iWarWeariness / 3;
}
}

// Danger considerations
int iOurMultiplier = 1;
int iTheirMultiplier = 1;

if (!bInTerribleShape || GetWarState(*it) == WAR_STATE_NEARLY_WON)
{
if (GetMilitaryStrengthComparedToUs(*it) < STRENGTH_AVERAGE)
{
iOurMultiplier++;
}
if (IsEasyTarget(*it))
{
iOurMultiplier++;
}
if (GetPlayer()->GetMilitaryAI()->HavePreferredAttackTarget(*it))
{
iOurMultiplier++;
}
if (GetWarState(*it) == WAR_STATE_NEARLY_WON || GET_PLAYER(*it).IsInTerribleShapeForWar())
{
iOurMultiplier++;
}
}
if (!GET_PLAYER(*it).IsInTerribleShapeForWar())
{
if (GetMilitaryStrengthComparedToUs(*it) > STRENGTH_AVERAGE)
{
iTheirMultiplier++;
}
if (bInTerribleShape)
{
iTheirMultiplier++;
}
if (GET_PLAYER(*it).GetDiplomacyAI()->IsEasyTarget(GetID()))
{
iTheirMultiplier++;
}
if (GetPlayer()->GetMilitaryAI()->IsExposedToEnemy(NULL, *it))
{
iTheirMultiplier++;
}
}

// They're in danger and we're not? Let's hold out longer!
if (iOurDanger == 0 && iTheirDanger > 0)
{
if (iWarDuration < 20 || iOurMultiplier > 2 || bSeriousDangerThem)
{
iPeaceScore -= (5 * iTheirDanger * iOurMultiplier);
}
else
{
iPeaceScore -= (2 * iTheirDanger * iOurMultiplier);
}
}
// We're in danger and they're not? Let's hold out less!
else if (iOurDanger > 0 && iTheirDanger == 0)
{
if (iWarScore <= GetWarscoreThresholdNegative()/2 || iTheirMultiplier > 2 || bSeriousDangerUs)
{
iPeaceScore += (5 * iOurDanger * iTheirMultiplier);
}
else
{
iPeaceScore += (2 * iOurDanger * iTheirMultiplier);
}
}
// We're both in danger - how much?
else
{
iPeaceScore -= (2 * iTheirDanger * iOurMultiplier);
iPeaceScore += (2 * iOurDanger * iTheirMultiplier);
}

// Also consider the overall war state
bool bLosingOverall = GetStateAllWars() == STATE_ALL_WARS_LOSING;

switch (GetWarState(*it))
{
case NO_WAR_STATE_TYPE:
UNREACHABLE(); // Being here would indicate we aren't at a war with this player.
case WAR_STATE_NEARLY_WON:
iPeaceScore -= bReadyForVassalage ? 0 : 10 * iOurMultiplier;
break;
case WAR_STATE_OFFENSIVE:
iPeaceScore -= 5 * iOurMultiplier;
break;
case WAR_STATE_CALM:
iPeaceScore -= bLosingOverall ? -5 * iTheirMultiplier : 2 * iOurMultiplier;
break;
case WAR_STATE_STALEMATE:
case WAR_STATE_TROUBLED:
iPeaceScore += bLosingOverall ? 5 * iTheirMultiplier : 2 * iTheirMultiplier;
break;
case WAR_STATE_DEFENSIVE:
case WAR_STATE_NEARLY_DEFEATED:
iPeaceScore += bLosingOverall ? 10 * iTheirMultiplier : 5 * iTheirMultiplier;
break;
}

// Hold out for longer if we have military support from nearby allies
int iAlliesMod = 0;
if (!bInTerribleShape)
{
for (std::vector<PlayerTypes>::iterator iter = vOurWarAllies.begin(); iter != vOurWarAllies.end(); iter++)
{
if (GET_PLAYER(*iter).GetID() == GetID())
continue;
if (!GET_PLAYER(*iter).isMajorCiv())
continue;
if (GET_PLAYER(*iter).IsInTerribleShapeForWar())
continue;
if (GET_PLAYER(*iter).GetProximityToPlayer(GetID()) < PLAYER_PROXIMITY_CLOSE)
continue;
if (GET_PLAYER(*iter).GetProximityToPlayer(*it) < PLAYER_PROXIMITY_CLOSE)
continue;
if (GET_PLAYER(*iter).GetDiplomacyAI()->GetWarState(*it) <= WAR_STATE_TROUBLED)
continue;

switch (GET_PLAYER(*iter).GetDiplomacyAI()->GetMilitaryStrengthComparedToUs(*it))
{
case NO_STRENGTH_VALUE:
UNREACHABLE(); // Strengths are supposed to have been evaluated by this point.
case STRENGTH_IMMENSE:
case STRENGTH_POWERFUL:
case STRENGTH_STRONG:
break;
case STRENGTH_AVERAGE:
case STRENGTH_POOR:
iAlliesMod -= 5;
break;
case STRENGTH_WEAK:
iAlliesMod -= 10;
break;
case STRENGTH_PATHETIC:
iAlliesMod -= 15;
break;
}
}
// Ongoing coop war? Minimum -10 modifier.
if (iAlliesMod > -10 && GetGlobalCoopWarAgainstState(*it) == COOP_WAR_STATE_ONGOING)
{
iAlliesMod = -10;
}
}
if (iWarScore <= GetWarscoreThresholdNegative() && GetWarState(*it) < WAR_STATE_OFFENSIVE)
{
iAlliesMod /= 2;
}

iPeaceScore += iAlliesMod;

// If we want to conquer them, let's hold out longer.
{
iPeaceScore -= 10;
}

// If they captured one of our key cities, let's hold out for significantly longer.
if (bCapturedKeyCity)
{
iPeaceScore -= 15;
}

// If they're about to win the game, let's hold out for a lot longer.
if (IsEndgameAggressiveTo(*it))
{
iPeaceScore -= 20;
}

bool bAztecException = false;

if (iPeaceScore > 0)
{
// If we're going for world conquest, we want to fight our wars until we get their capital or can vassalize them
// We should also be more reluctant to make peace if they've captured cities from us, or cities that we want to liberate
// However, do not factor this in when losing
if (!bInTerribleShape)
{
if (iWarScore > 0 || (iWarScore > GetWarscoreThresholdNegative()/2 && GetRawTargetValue(*it) >= TARGET_VALUE_AVERAGE && GetWarState(*it) > WAR_STATE_TROUBLED))
{
if (iWarScore < WARSCORE_THRESHOLD_POSITIVE && GetPlayer()->GetPlayerTraits()->GetGoldenAgeFromVictory() > 0)
{
bAztecException = true;
iPeaceScore /= 2;
}
else if (bCapturedAnyCityFromUs)
{
if (iWarScore > (iWarDuration / 2))
iPeaceScore /= 2;
else
{
iPeaceScore *= 75;
iPeaceScore /= 100;
}
}
else if (bCapturedAnyCityWeWantToLiberate)
{
if ((bDiplomatic && iWarScore > 0) || iWarScore > iWarDuration)
{
if (bDiplomatic && iWarScore > iWarDuration)
{
iPeaceScore /= 2;
}
else
{
iPeaceScore *= 75;
iPeaceScore /= 100;
}
}
}
else if (bWorldConquest)
{
if (bReadyForVassalage || (GET_PLAYER(*it).GetCapitalConqueror() != NO_PLAYER && GET_PLAYER(*it).GetNumCapitalCities() <= 0))
{
// If they're economically weak or a bad target, boost peace willingness
if (GetEconomicStrengthComparedToUs(*it) <= STRENGTH_WEAK || GetRawTargetValue(*it) <= TARGET_VALUE_BAD)
iPeaceScore *= 2;
else if (GetTargetValue(*it) == TARGET_VALUE_DIFFICULT)
{
iPeaceScore *= 3;
iPeaceScore /= 2;
}
}
else if (GET_PLAYER(*it).GetCapitalConqueror() == NO_PLAYER || GET_PLAYER(*it).GetNumCapitalCities() > 0)
{
iPeaceScore *= 75;
iPeaceScore /= 100;
}
}
}
}
// Overall state is bad and we're doing great in this war, or at least haven't lost any cities? Let's peace out!
else if (!bCapturedAnyCityFromUs || bReadyForVassalage || GetWarState(*it) == WAR_STATE_NEARLY_WON)
{
iPeaceScore *= 2;

// Both? Even more likely to make peace!
if (!bCapturedAnyCityFromUs && (bReadyForVassalage || GetWarState(*it) == WAR_STATE_NEARLY_WON))
iPeaceScore *= 2;
}

// Modify based on leader flavors
// High Meanness leaders will fight to the bitter end when losing, high Diplo Balance leaders like to return to status quo when winning
if (iWarScore > 0)
{
}
else if (iWarScore < 0)
{
}
}

// Must be high enough to return a true desire for peace
int iWarCount = GetPlayer()->CountNumDangerousMajorsAtWarWith(true, false) - 1;
if (iWarCount <= 0)
iWarCount = 0;

bool bWantedPeaceLastTime = IsWantsPeaceWithPlayer(*it) && !bAztecException; // if we were willing to make peace last update, add some leeway here to prevent flip-flopping
int iThreshold = bWantedPeaceLastTime ? /*17*/ GD_INT_GET(REQUEST_PEACE_LEEWAY_THRESHOLD) : /*20*/ GD_INT_GET(REQUEST_PEACE_TURN_THRESHOLD);
int iThresholdReductionPerOtherWar = bWantedPeaceLastTime ? /*3*/ GD_INT_GET(REQUEST_PEACE_LEEWAY_THRESHOLD_REDUCTION_PER_WAR) : /*2*/ GD_INT_GET(REQUEST_PEACE_THRESHOLD_REDUCTION_PER_WAR);

iThreshold = max(iThreshold - max(iThresholdReductionPerOtherWar * iWarCount, 0), 1);

if (iPeaceScore >= iThreshold)
vMakePeacePlayers.push_back(*it);
else
{
SetTreatyWillingToOffer(*it, NO_PEACE_TREATY_TYPE);
SetTreatyWillingToAccept(*it, NO_PEACE_TREATY_TYPE);
}

if (bLog)
{
CvString strOutBuf;
CvString strBaseString;
CvString playerName;
CvString otherPlayerName;
CvString strLogName;

// Find the name of this civ and city
playerName = m_pPlayer->getCivilizationShortDescription();

// Open the log file
if (GC.getPlayerAndCityAILogSplit())
{
strLogName = "DiplomacyAI_Peace_Log" + playerName + ".csv";
}
else
{
strLogName = "DiplomacyAI_Peace_Log.csv";
}

FILogFile* pLog = NULL;
pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp);

// Get the leading info for this line
strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns());
otherPlayerName = GET_PLAYER(*it).getCivilizationShortDescription();
strBaseString += playerName + " VS. " + otherPlayerName;

if (iPeaceScore >= iThreshold)
strOutBuf.Format("Willing to make peace, Score for Peace: %d, Required Score: %d", iPeaceScore, iThreshold);
else
strOutBuf.Format("Not willing to make peace, Score for Peace: %d, Required Score: %d", iPeaceScore, iThreshold);

strBaseString += strOutBuf;
pLog->Msg(strBaseString);
}
}

// STEP 4: Decide WHAT we're willing to accept/offer in exchange for peace.
for (std::vector<PlayerTypes>::iterator it = vMakePeacePlayers.begin(); it != vMakePeacePlayers.end(); it++)
{
PeaceTreatyTypes eTreatyWillingToOffer = PEACE_TREATY_WHITE_PEACE;
PeaceTreatyTypes eTreatyWillingToAccept = PEACE_TREATY_WHITE_PEACE;
int iWillingToOfferScore = 0;
int iWillingToAcceptScore = 0;

// What we're willing to offer/accept in a peace treaty.
int iWarScore = GetWarScore(*it);

// Negative Warscore? Offer more.
if (iWarScore < 0)
{
iWillingToOfferScore -= iWarScore;
}
// Positive Warscore? Accept more.
else
{
iWillingToAcceptScore += iWarScore;
}

// War Weary? We're more willing to make peace.
if (MOD_BALANCE_VP)
{
if (iWarScore <= 0)
{
iWillingToOfferScore += GetPlayer()->GetUnhappinessFromWarWeariness();
}
else
{
iWillingToAcceptScore -= GetPlayer()->GetUnhappinessFromWarWeariness();
}
}

// Do the final assessment
if (iWillingToOfferScore >= /*100*/ GD_INT_GET(PEACE_WILLINGNESS_OFFER_THRESHOLD_UN_SURRENDER))
eTreatyWillingToOffer = PEACE_TREATY_UNCONDITIONAL_SURRENDER;
else if (iWillingToOfferScore >= /*90*/ GD_INT_GET(PEACE_WILLINGNESS_OFFER_THRESHOLD_CAPITULATION))
eTreatyWillingToOffer = PEACE_TREATY_CAPITULATION;
else if (iWillingToOfferScore >= /*80*/ GD_INT_GET(PEACE_WILLINGNESS_OFFER_THRESHOLD_CESSION))
eTreatyWillingToOffer = PEACE_TREATY_CESSION;
else if (iWillingToOfferScore >= /*70*/ GD_INT_GET(PEACE_WILLINGNESS_OFFER_THRESHOLD_SURRENDER))
eTreatyWillingToOffer = PEACE_TREATY_SURRENDER;
else if (iWillingToOfferScore >= /*60*/ GD_INT_GET(PEACE_WILLINGNESS_OFFER_THRESHOLD_SUBMISSION))
eTreatyWillingToOffer = PEACE_TREATY_SUBMISSION;
else if (iWillingToOfferScore >= /*40*/ GD_INT_GET(PEACE_WILLINGNESS_OFFER_THRESHOLD_BACKDOWN))
eTreatyWillingToOffer = PEACE_TREATY_BACKDOWN;
else if (iWillingToOfferScore >= /*30*/ GD_INT_GET(PEACE_WILLINGNESS_OFFER_THRESHOLD_SETTLEMENT))
eTreatyWillingToOffer = PEACE_TREATY_SETTLEMENT;
else if (iWillingToOfferScore >= /*15*/ GD_INT_GET(PEACE_WILLINGNESS_OFFER_THRESHOLD_ARMISTICE))
eTreatyWillingToOffer = PEACE_TREATY_ARMISTICE;

// Do the final assessment
if (iWillingToAcceptScore >= /*100*/ GD_INT_GET(PEACE_WILLINGNESS_ACCEPT_THRESHOLD_UN_SURRENDER))
eTreatyWillingToAccept = PEACE_TREATY_UNCONDITIONAL_SURRENDER;
else if (iWillingToAcceptScore >= /*90*/ GD_INT_GET(PEACE_WILLINGNESS_ACCEPT_THRESHOLD_CAPITULATION))
eTreatyWillingToAccept = PEACE_TREATY_CAPITULATION;
else if (iWillingToAcceptScore >= /*80*/ GD_INT_GET(PEACE_WILLINGNESS_ACCEPT_THRESHOLD_CESSION))
eTreatyWillingToAccept = PEACE_TREATY_CESSION;
else if (iWillingToAcceptScore >= /*70*/ GD_INT_GET(PEACE_WILLINGNESS_ACCEPT_THRESHOLD_SURRENDER))
eTreatyWillingToAccept = PEACE_TREATY_SURRENDER;
else if (iWillingToAcceptScore >= /*60*/ GD_INT_GET(PEACE_WILLINGNESS_ACCEPT_THRESHOLD_SUBMISSION))
eTreatyWillingToAccept = PEACE_TREATY_SUBMISSION;
else if (iWillingToAcceptScore >= /*40*/ GD_INT_GET(PEACE_WILLINGNESS_ACCEPT_THRESHOLD_BACKDOWN))
eTreatyWillingToAccept = PEACE_TREATY_BACKDOWN;
else if (iWillingToAcceptScore >= /*30*/ GD_INT_GET(PEACE_WILLINGNESS_ACCEPT_THRESHOLD_SETTLEMENT))
eTreatyWillingToAccept = PEACE_TREATY_SETTLEMENT;
else if (iWillingToAcceptScore >= /*15*/ GD_INT_GET(PEACE_WILLINGNESS_ACCEPT_THRESHOLD_ARMISTICE))
eTreatyWillingToAccept = PEACE_TREATY_ARMISTICE;

// If we're in a critical state, always accept a white peace.
if (bCriticalState)
{
if (eTreatyWillingToOffer <= PEACE_TREATY_WHITE_PEACE)
eTreatyWillingToOffer = PEACE_TREATY_WHITE_PEACE;

eTreatyWillingToAccept = PEACE_TREATY_WHITE_PEACE;
}

SetTreatyWillingToOffer(*it, eTreatyWillingToOffer);
SetTreatyWillingToAccept(*it, eTreatyWillingToAccept);
}
}``````

Reasons why peace might be blocked:
Code:
``````static void FmtPeaceBlockReasonLogStr(CvString& str, PlayerTypes eThisPlayer, PlayerTypes eAtWarPlayer, PeaceBlockReasons eReason)
{
switch (eReason)
{
case NO_PEACE_BLOCK_REASON:
UNREACHABLE(); // Peace is expected to be blocked.
case PEACE_BLOCK_REASON_ALWAYS_WAR:
str.Format("We are always at war with this player!");
break;
case PEACE_BLOCK_REASON_VASSALAGE:
str.Format("One of us is a vassal!");
break;
case PEACE_BLOCK_REASON_TOO_SOON:
str.Format("Too early to make peace!");
break;
case PEACE_BLOCK_REASON_CITY_JUST_CAPTURED:
str.Format("Our city was just captured and we have units near the enemy's cities!");
break;
case PEACE_BLOCK_REASON_AT_WAR_WITH_ALLY:
str.Format("They're at war with our ally!");
break;
case PEACE_BLOCK_REASON_WAR_DEAL:
str.Format("Locked into coop/3rd party war for %d more turns!", GET_TEAM(GET_PLAYER(eThisPlayer).getTeam()).GetNumTurnsLockedIntoWar(GET_PLAYER(eAtWarPlayer).getTeam()));
break;
case PEACE_BLOCK_REASON_NO_ENEMY_CAPITAL:
str.Format("Other player has no cities so we lose nothing from maintaining the war!");
break;
case PEACE_BLOCK_REASON_COOP_WAR_AGAINST_DEFENSIVE_PACT:
str.Format("We're planning a coop war against someone they have a Defensive Pact with! Making peace would cancel the coop war!");
break;
case PEACE_BLOCK_REASON_SCENARIO:
str.Format("Cannot change war/peace status due to special scenario rule!");
break;
}
}``````

There is special logic on whether or not the AI is willing to capitulate if they have team members. I'll include it if you're curious.
Code:
``````    for (size_t i=0; i<vOurTeam.size(); i++)
{
if (!GET_PLAYER(vOurTeam[i]).isAlive() || !GET_PLAYER(vOurTeam[i]).isMajorCiv() || GET_PLAYER(vOurTeam[i]).getNumCities() <= 0)
continue;

bool bYes = false;

for (size_t j=0; j<vTheirTeam.size(); j++)
{
if (!GET_PLAYER(vTheirTeam[j]).isAlive() || !GET_PLAYER(vTheirTeam[j]).isMajorCiv() || GET_PLAYER(vTheirTeam[j]).getNumCities() <= 0)
continue;

// If we're at war, this is capitulation - otherwise, it's voluntary vassalage
// If we agree to become the vassal of any of their team members, then we agree to become the vassal of all of them
if (IsAtWar(ePlayer))
{
if (GET_PLAYER(vOurTeam[i]).GetDiplomacyAI()->IsCapitulationAcceptable(vTheirTeam[j]))
{
bYes = true;
iNumTeamMembersInFavor++;
break;
}
}
else
{
if (GET_PLAYER(vOurTeam[i]).GetDiplomacyAI()->IsVoluntaryVassalageAcceptable(vTheirTeam[j]))
{
bYes = true;
iNumTeamMembersInFavor++;
break;
}
}
}

if (!bYes)
iNumTeamMembersAgainst++;
}

// More team members must agree than refuse
return iNumTeamMembersInFavor > 0 && iNumTeamMembersInFavor > iNumTeamMembersAgainst;``````

Last edited:
There is no special logic for teams re: making peace. If any AI member of the team is willing to make peace, you can make peace with the entire team that way. Likewise, if the AI is willing to make peace with one player, they will make peace with all players on a team.

One exception: the AI will never make peace (or capitulate) if it's on the same team as a human player. The human makes all the decisions. They also won't agree to accept Embassies or trade Open Borders, Defensive Pacts or Research Agreements.

There is a lot of code here, so I can't reproduce all of it, but feel free to ask any more specific questions.

Function to determine peace willingness:
Code:
``````void CvDiplomacyAI::DoUpdatePeaceTreatyWillingness(bool bMyTurn)
{
CvCity* pCapital = GetPlayer()->getCapitalCity();
bool bLog = bMyTurn && GC.getLogging() && GC.getAILogging(); // Only log this once per turn to prevent log spam
int iLoop = 0;

if (GC.getGame().isOption(GAMEOPTION_ALWAYS_WAR) || GC.getGame().isOption(GAMEOPTION_NO_CHANGING_WAR_PEACE) || GetPlayer()->isHuman() || GetPlayer()->IsAITeammateOfHuman() || GetPlayer()->IsVassalOfSomeone())
{
for (int iPlayerLoop = 0; iPlayerLoop < MAX_MAJOR_CIVS; iPlayerLoop++)
{
PlayerTypes ePlayer = (PlayerTypes) iPlayerLoop;
SetTreatyWillingToOffer(ePlayer, NO_PEACE_TREATY_TYPE);
SetTreatyWillingToAccept(ePlayer, NO_PEACE_TREATY_TYPE);
}
if (bLog && !GetPlayer()->isHuman())
{
CvString strOutBuf;
CvString strBaseString;
CvString playerName;
CvString strLogName;

// Find the name of this civ and city
playerName = m_pPlayer->getCivilizationShortDescription();

// Open the log file
if (GC.getPlayerAndCityAILogSplit())
{
strLogName = "DiplomacyAI_Peace_Log" + playerName + ".csv";
}
else
{
strLogName = "DiplomacyAI_Peace_Log.csv";
}

FILogFile* pLog = NULL;
pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp);

// Get the leading info for this line
strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns());
strBaseString += playerName + " VS. Everyone";

strOutBuf.Format("Not allowed to change war/peace status with anyone!");

strBaseString += strOutBuf;
pLog->Msg(strBaseString);
}
return;
}

// STEP 1: See if we're in a critical state and check which players are valid
bool bCriticalState = GetPlayer()->IsEmpireSuperUnhappy() || !pCapital || pCapital->isInDangerOfFalling(true) || pCapital->getDamage() >= (pCapital->GetMaxHitPoints()/2);
bool bMakePeaceWithAllMinors = false;
bool bWorldConquest = IsGoingForWorldConquest() || IsCloseToWorldConquest();
bool bDiplomatic = IsGoingForDiploVictory() || IsDiplomat() || GetPlayer()->GetPlayerTraits()->IsDiplomat();
vector<PlayerTypes> vValidPlayers;

for (int iPlayerLoop = 0; iPlayerLoop < MAX_CIV_PLAYERS; iPlayerLoop++)
{
PlayerTypes eLoopPlayer = (PlayerTypes) iPlayerLoop;

if (GET_PLAYER(eLoopPlayer).isAlive() && IsAtWar(eLoopPlayer))
{
if (GET_PLAYER(eLoopPlayer).isMajorCiv())
{
if (IsPeaceBlocked(eLoopPlayer))
{
SetTreatyWillingToOffer(eLoopPlayer, NO_PEACE_TREATY_TYPE);
SetTreatyWillingToAccept(eLoopPlayer, NO_PEACE_TREATY_TYPE);

if (bLog)
{
CvString strOutBuf;
CvString strBaseString;
CvString playerName;
CvString otherPlayerName;
CvString strLogName;
CvString strPeaceBlockReason;

FmtPeaceBlockReasonLogStr(strPeaceBlockReason, GetID(), eLoopPlayer, GetPeaceBlockReason(eLoopPlayer));

// Find the name of this civ and city
playerName = m_pPlayer->getCivilizationShortDescription();

// Open the log file
if (GC.getPlayerAndCityAILogSplit())
{
strLogName = "DiplomacyAI_Peace_Log" + playerName + ".csv";
}
else
{
strLogName = "DiplomacyAI_Peace_Log.csv";
}

FILogFile* pLog = NULL;
pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp);

// Get the leading info for this line
strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns());
otherPlayerName = GET_PLAYER(eLoopPlayer).getCivilizationShortDescription();
strBaseString += playerName + " VS. " + otherPlayerName;

strOutBuf.Format("PEACE BLOCKED! ");
strOutBuf += strPeaceBlockReason;

strBaseString += strOutBuf;
pLog->Msg(strBaseString);
}
}
else
{
vValidPlayers.push_back(eLoopPlayer);
}
}

if (!bCriticalState)
{
WarStateTypes eWarState = GetWarState(eLoopPlayer);

if (eWarState == WAR_STATE_NEARLY_DEFEATED)
{
bCriticalState = true;
}
else if (eWarState <= WAR_STATE_TROUBLED)
{
if (GetPlayer()->IsNoNewWars() || !GET_PLAYER(eLoopPlayer).isMajorCiv() || !IsPhonyWar(eLoopPlayer) || GetWarScore(eLoopPlayer) <= WARSCORE_THRESHOLD_NEGATIVE)
bMakePeaceWithAllMinors = true;
}
}
}
else if (GET_PLAYER(eLoopPlayer).isMajorCiv())
{
SetTreatyWillingToOffer(eLoopPlayer, NO_PEACE_TREATY_TYPE);
SetTreatyWillingToAccept(eLoopPlayer, NO_PEACE_TREATY_TYPE);
}
}
if (bCriticalState)
bMakePeaceWithAllMinors = true;

// STEP 2: Make peace with any City-States here - this happens instantly!
if (bMyTurn)
{
for (int iPlayerLoop = MAX_MAJOR_CIVS; iPlayerLoop < MAX_CIV_PLAYERS; iPlayerLoop++)
{
PlayerTypes eMinor = (PlayerTypes) iPlayerLoop;

if (IsAtWar(eMinor))
{
if (IsPeaceBlocked(eMinor))
{
if (bLog)
{
CvString strOutBuf;
CvString strBaseString;
CvString playerName;
CvString otherPlayerName;
CvString strLogName;
CvString strPeaceBlockReason;

FmtPeaceBlockReasonLogStr(strPeaceBlockReason, GetID(), eMinor, GetPeaceBlockReason(eMinor));

// Find the name of this civ and city
playerName = m_pPlayer->getCivilizationShortDescription();

// Open the log file
if (GC.getPlayerAndCityAILogSplit())
{
strLogName = "DiplomacyAI_Peace_Log" + playerName + ".csv";
}
else
{
strLogName = "DiplomacyAI_Peace_Log.csv";
}

FILogFile* pLog = NULL;
pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp);

// Get the leading info for this line
strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns());
otherPlayerName = GET_PLAYER(eMinor).getCivilizationShortDescription();
strBaseString += playerName + " VS. " + otherPlayerName;

strOutBuf.Format("PEACE BLOCKED! ");
strOutBuf += strPeaceBlockReason;

strBaseString += strOutBuf;
pLog->Msg(strBaseString);
}
}
else
{
if (bMakePeaceWithAllMinors || GetCSWarTargetPlayer() != eMinor)
{
if (!bCriticalState)
{
// Exception if making peace with this City-State would prevent us from achieving Domination Victory
if (GC.getGame().WouldMakingPeacePreventDominationVictory(GetID(), eMinor))
{
if (IsGoingForWorldConquest() || IsCloseToWorldConquest())
continue;
}

// Exception under two circumstances: the minor's capital or a city they captured from us are in danger from us
for (CvCity* pLoopCity = GET_PLAYER(eMinor).firstCity(&iLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER(eMinor).nextCity(&iLoop))
{
if (pLoopCity->isCapital() || GET_PLAYER(pLoopCity->getOriginalOwner()).getTeam() == GetTeam())
{
if (pLoopCity->IsInDanger(GetID()) && (pLoopCity->isUnderSiege() || pLoopCity->IsBlockadedWaterAndLand()))
{
continue;
}
}
}
}

GET_TEAM(GetTeam()).makePeace(GET_PLAYER(eMinor).getTeam(), true, false, GetID());

if (bLog)
{
CvString strOutBuf;
CvString strBaseString;
CvString playerName;
CvString otherPlayerName;
CvString strLogName;

// Find the name of this civ and city
playerName = m_pPlayer->getCivilizationShortDescription();

// Open the log file
if (GC.getPlayerAndCityAILogSplit())
{
strLogName = "DiplomacyAI_Peace_Log" + playerName + ".csv";
}
else
{
strLogName = "DiplomacyAI_Peace_Log.csv";
}

FILogFile* pLog = NULL;
pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp);

// Get the leading info for this line
strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns());
otherPlayerName = GET_PLAYER(eMinor).getCivilizationShortDescription();
strBaseString += playerName + " VS. " + otherPlayerName;

if (bCriticalState)
strOutBuf.Format("Making peace with all possible City-States because we're in a critical state!");
else if (bMakePeaceWithAllMinors)
strOutBuf.Format("Making peace with all possible City-States because we're doing poorly in war!");
else
strOutBuf.Format("This City-State is no longer our CS war target!");

strBaseString += strOutBuf;
pLog->Msg(strBaseString);
}
}
}
}
}
}

// STEP 3: Determine whether we're willing to make peace with any majors
vector<PlayerTypes> vMakePeacePlayers;
bool bInTerribleShape = false;
bool bUABonusesFromCityConquest = false;
if (!bCriticalState)
{
bInTerribleShape = GetPlayer()->IsInTerribleShapeForWar();
CvPlayerTraits* pTraits = GetPlayer()->GetPlayerTraits();
if (!bInTerribleShape)
{
// Assyria, Japan, Indonesia, The Aztecs, Nuclear Gandhi, and anyone who keeps conquered buildings will prolong wars longer if they are winning and the enemy's cities are in danger from them.
bUABonusesFromCityConquest = pTraits->IsTechFromCityConquer();
bUABonusesFromCityConquest |= pTraits->GetCityConquestGWAM() > 0;
bUABonusesFromCityConquest |= pTraits->GetUniqueLuxuryQuantity() > 0;
bUABonusesFromCityConquest |= pTraits->GetGoldenAgeFromVictory() > 0;
bUABonusesFromCityConquest |= pTraits->IsKeepConqueredBuildings() || GetPlayer()->IsKeepConqueredBuildings();
bUABonusesFromCityConquest |= pTraits->GetCultureBonusModifierConquest() > 0;
bUABonusesFromCityConquest |= pTraits->GetProductionBonusModifierConquest() > 0;
bUABonusesFromCityConquest |= pTraits->IsFreeGreatWorkOnConquest();
bUABonusesFromCityConquest |= pTraits->IsConquestOfTheWorld();
bUABonusesFromCityConquest |= IsNuclearGandhi();
}
}

for (std::vector<PlayerTypes>::iterator it = vValidPlayers.begin(); it != vValidPlayers.end(); it++)
{
if (bCriticalState)
{
vMakePeacePlayers.push_back(*it);
if (bLog)
{
CvString strOutBuf;
CvString strBaseString;
CvString playerName;
CvString otherPlayerName;
CvString strLogName;

// Find the name of this civ and city
playerName = m_pPlayer->getCivilizationShortDescription();

// Open the log file
if (GC.getPlayerAndCityAILogSplit())
{
strLogName = "DiplomacyAI_Peace_Log" + playerName + ".csv";
}
else
{
strLogName = "DiplomacyAI_Peace_Log.csv";
}

FILogFile* pLog = NULL;
pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp);

// Get the leading info for this line
strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns());
otherPlayerName = GET_PLAYER(*it).getCivilizationShortDescription();
strBaseString += playerName + " VS. " + otherPlayerName;

strOutBuf.Format("Making peace with all possible civilizations because we're in a critical state!");

strBaseString += strOutBuf;
pLog->Msg(strBaseString);
}
continue;
}

// Let's see how much danger each of us are currently in ...
int iWarDuration = min(GetPlayer()->GetPlayerNumTurnsAtWar(*it), GetPlayer()->GetPlayerNumTurnsSinceCityCapture(*it));
int iWarScore = GetWarScore(*it);
int iOurDanger = 0;
int iTheirDanger = 0;
bool bSeriousDangerUs = false;
bool bSeriousDangerThem = false;
ReligionTypes eMyReligion = GetPlayer()->GetReligions()->GetOwnedReligion();
ReligionTypes eTheirReligion = GET_PLAYER(*it).GetReligions()->GetOwnedReligion();
vector<PlayerTypes> vOurWarAllies = GetOffensiveWarAllies(*it, /*bIncludeMinors*/ true, /*bReverseMode*/ false);
vector<PlayerTypes> vTheirWarAllies = GetOffensiveWarAllies(*it, /*bIncludeMinors*/ true, /*bReverseMode*/ true);
vOurWarAllies.push_back(GetID());
vTheirWarAllies.push_back(*it);
vector<PlayerTypes> vMyTeam = GET_TEAM(GetTeam()).getPlayers();
vector<int> vEnemyCitiesEndangered;
vector<int> vEnemyCitiesEndangeredByUs;

for (CvCity* pLoopCity = m_pPlayer->firstCity(&iLoop); pLoopCity != NULL; pLoopCity = m_pPlayer->nextCity(&iLoop))
{
if (pLoopCity->IsInDangerFromPlayers(vTheirWarAllies)) // Only care if we're in danger from them!
{
int iDangerMod = 1;

//look at the tactical map (is it up to date?)
CvTacticalDominanceZone* pLandZone = m_pPlayer->GetTacticalAI()->GetTacticalAnalysisMap()->GetZoneByCity(pLoopCity,false);
CvTacticalDominanceZone* pWaterZone = m_pPlayer->GetTacticalAI()->GetTacticalAnalysisMap()->GetZoneByCity(pLoopCity,true);
if (pLandZone && pLandZone->GetOverallDominanceFlag()==TACTICAL_DOMINANCE_ENEMY)
iDangerMod++;
if (pWaterZone && pWaterZone->GetOverallDominanceFlag()==TACTICAL_DOMINANCE_ENEMY)
iDangerMod++;

if (pLoopCity->isUnderSiege())
{
if (pLoopCity->isInDangerOfFalling(true))
iDangerMod += 3;
else
iDangerMod++;

if (pLoopCity->isInDangerOfFalling(true) || pLoopCity->getDamage() >= (pLoopCity->GetMaxHitPoints()/2))
{
if (pLoopCity->getOriginalOwner() == GetID())
{
bSeriousDangerUs = true;
}
else if (pLoopCity->isCapital() || pLoopCity->IsOriginalMajorCapital() || (eMyReligion != NO_RELIGION && pLoopCity->GetCityReligions()->IsHolyCityForReligion(eMyReligion)) || (eTheirReligion != NO_RELIGION && pLoopCity->GetCityReligions()->IsHolyCityForReligion(eTheirReligion))
|| pLoopCity->HasAnyWonder() || pLoopCity->getNumNationalWonders() > 0)
{
bSeriousDangerUs = true;
}
}
}

if (iDangerMod > 1 && pLoopCity->IsInDanger(*it) && GetPlayer()->GetMilitaryAI()->IsExposedToEnemy(pLoopCity, *it))
{
iDangerMod++;
}

if (pLoopCity->isCapital())
iDangerMod *= 3;
else if (pLoopCity->IsOriginalMajorCapital() || (eMyReligion != NO_RELIGION && pLoopCity->GetCityReligions()->IsHolyCityForReligion(eMyReligion)) || (eTheirReligion != NO_RELIGION && pLoopCity->GetCityReligions()->IsHolyCityForReligion(eTheirReligion)) || pLoopCity->HasAnyWonder())
iDangerMod *= 2;
else if (pLoopCity->GetCityReligions()->IsHolyCityAnyReligion() || pLoopCity->getNumNationalWonders() > 0)
iDangerMod++;

iOurDanger += iDangerMod;
}
}

for (CvCity* pLoopCity = GET_PLAYER(*it).firstCity(&iLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER(*it).nextCity(&iLoop))
{
if (pLoopCity->IsInDangerFromPlayers(vOurWarAllies)) // Only care if they're in danger from us!
{
int iDangerMod = 1;

//look at the tactical map (is it up to date?)
CvTacticalDominanceZone* pLandZone = GET_PLAYER(*it).GetTacticalAI()->GetTacticalAnalysisMap()->GetZoneByCity(pLoopCity,false);
CvTacticalDominanceZone* pWaterZone = GET_PLAYER(*it).GetTacticalAI()->GetTacticalAnalysisMap()->GetZoneByCity(pLoopCity,true);

if (pLandZone && pLandZone->GetOverallDominanceFlag()==TACTICAL_DOMINANCE_ENEMY)
iDangerMod++;
if (pWaterZone && pWaterZone->GetOverallDominanceFlag()==TACTICAL_DOMINANCE_ENEMY)
iDangerMod++;

if (pLoopCity->isUnderSiege())
{
if (pLoopCity->isInDangerOfFalling(true))
iDangerMod += 3;
else
iDangerMod++;

if (pLoopCity->isInDangerOfFalling(true) || pLoopCity->getDamage() >= (pLoopCity->GetMaxHitPoints()/2))
{
bSeriousDangerThem = true;
}
}

if (iDangerMod > 1)
{
vEnemyCitiesEndangered.push_back(pLoopCity->GetID());

if (pLoopCity->IsInDangerFromPlayers(vMyTeam))
{
vEnemyCitiesEndangeredByUs.push_back(pLoopCity->GetID());

if (pLoopCity->IsInDanger(GetID()) && GetPlayer()->GetMilitaryAI()->IsPreferredAttackTarget(pLoopCity))
{
iDangerMod++;
}
}
}

if (pLoopCity->isCapital())
iDangerMod *= 3;
else if (pLoopCity->IsOriginalMajorCapital() || (eMyReligion != NO_RELIGION && pLoopCity->GetCityReligions()->IsHolyCityForReligion(eMyReligion)) || (eTheirReligion != NO_RELIGION && pLoopCity->GetCityReligions()->IsHolyCityForReligion(eTheirReligion)) || pLoopCity->HasAnyWonder())
iDangerMod *= 2;
else if (pLoopCity->GetCityReligions()->IsHolyCityAnyReligion())
iDangerMod++;

iTheirDanger += iDangerMod;
}
}

bool bCapturedKeyCity = IsCapitalCapturedBy(*it, true, true) || IsHolyCityCapturedBy(*it, true, true);
bool bPhonyWar = IsPhonyWar(*it);
bool bCapturedAnyCityFromUs = bCapturedKeyCity;
bool bCapturedAnyCityWeWantToLiberate = false;
bool bReadyForVassalage = iWarScore >= 75 && !bCapturedKeyCity && !IsEndgameAggressiveTo(*it) && GET_TEAM(GET_PLAYER(*it).getTeam()).canBecomeVassal(GetTeam()) && !IsUntrustworthy(*it);

if (!bInTerribleShape)
{
// Let's check if they have any of our cities. If they do, no vassalage!
for (CvCity* pLoopCity = GET_PLAYER(*it).firstCity(&iLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER(*it).nextCity(&iLoop))
{
PlayerTypes eOriginalOwner = pLoopCity->getOriginalOwner();

if (GET_PLAYER(eOriginalOwner).getTeam() == GET_PLAYER(*it).getTeam())
continue;

CvPlot* pCityPlot = pLoopCity->plot();
if (!pCityPlot)
continue;

bool bEndangeredPeriod = std::find(vEnemyCitiesEndangered.begin(), vEnemyCitiesEndangered.end(), pLoopCity->GetID()) != vEnemyCitiesEndangered.end();
bool bEndangeredByOurTeam = bEndangeredPeriod && std::find(vEnemyCitiesEndangeredByUs.begin(), vEnemyCitiesEndangeredByUs.end(), pLoopCity->GetID()) != vEnemyCitiesEndangeredByUs.end();

// Ignore cities that are too far away from us (unless endangered).
if (GetPlayer()->GetCityDistanceInPlots(pCityPlot) > 12)
{
if (!bEndangeredByOurTeam && (!bEndangeredPeriod || GET_PLAYER(eOriginalOwner).getTeam() != GetTeam()))
continue;
}

if (GET_PLAYER(eOriginalOwner).getTeam() == GetTeam())
{
bCapturedAnyCityFromUs = true;
}
// Is this a city that we want to liberate?
else if (IsTryingToLiberate(pLoopCity))
{
bCapturedAnyCityWeWantToLiberate = true;
}

if (bCapturedAnyCityFromUs && bCapturedAnyCityWeWantToLiberate)
break;

// Is this a valuable city endangered by our team?
// Alternatively, if we're nasty/hateful or get bonuses from conquering cities, we're rarely willing to vassalize if ANY city is vulnerable.
{
bool bProceed = bUABonusesFromCityConquest || IsBackstabber() || GetMeanness() >= 8 || GetDiploBalance() <= 2 || GetCivOpinion(*it) == CIV_OPINION_UNFORGIVABLE;
bProceed |= pLoopCity->IsOriginalMajorCapital() || pLoopCity->GetCityReligions()->IsHolyCityAnyReligion() || pLoopCity->HasAnyWonder();

if (bProceed)
{
if (GetPlayer()->GetMilitaryAI()->IsPreferredAttackTarget(pLoopCity))
{
}
else if (pLoopCity->isUnderSiege())
{
}
else
{
//look at the tactical map (is it up to date?)
CvTacticalDominanceZone* pLandZone = GET_PLAYER(*it).GetTacticalAI()->GetTacticalAnalysisMap()->GetZoneByCity(pLoopCity,false);
CvTacticalDominanceZone* pWaterZone = GET_PLAYER(*it).GetTacticalAI()->GetTacticalAnalysisMap()->GetZoneByCity(pLoopCity,true);

if (pLandZone && pLandZone->GetOverallDominanceFlag()==TACTICAL_DOMINANCE_ENEMY)
{
}
else if (pWaterZone && pWaterZone->GetOverallDominanceFlag()==TACTICAL_DOMINANCE_ENEMY)
{
}
}
}
}
}
}

// No peace if they're in danger of losing a city and we're not.
if (bSeriousDangerThem && !bSeriousDangerUs)
{
if (iWarDuration < 40) // Failsafe in case of a problem somewhere - limit the amount of time peace is impossible for
{
SetTreatyWillingToOffer(*it, NO_PEACE_TREATY_TYPE);
SetTreatyWillingToAccept(*it, NO_PEACE_TREATY_TYPE);
if (bLog)
{
CvString strOutBuf;
CvString strBaseString;
CvString playerName;
CvString otherPlayerName;
CvString strLogName;

// Find the name of this civ and city
playerName = m_pPlayer->getCivilizationShortDescription();

// Open the log file
if (GC.getPlayerAndCityAILogSplit())
{
strLogName = "DiplomacyAI_Peace_Log" + playerName + ".csv";
}
else
{
strLogName = "DiplomacyAI_Peace_Log.csv";
}

FILogFile* pLog = NULL;
pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp);

// Get the leading info for this line
strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns());
otherPlayerName = GET_PLAYER(*it).getCivilizationShortDescription();
strBaseString += playerName + " VS. " + otherPlayerName;

strOutBuf.Format("No peace! They're in serious danger of losing a city and we're not!");

strBaseString += strOutBuf;
pLog->Msg(strBaseString);
}
continue;
}
}
// If we're in serious danger, let's offer peace.
else if ((iWarScore <= -90 && iOurDanger > 0) || (iWarScore <= -50 && (bInTerribleShape || bSeriousDangerUs)))
{
vMakePeacePlayers.push_back(*it);
if (bLog)
{
CvString strOutBuf;
CvString strBaseString;
CvString playerName;
CvString otherPlayerName;
CvString strLogName;

// Find the name of this civ and city
playerName = m_pPlayer->getCivilizationShortDescription();

// Open the log file
if (GC.getPlayerAndCityAILogSplit())
{
strLogName = "DiplomacyAI_Peace_Log" + playerName + ".csv";
}
else
{
strLogName = "DiplomacyAI_Peace_Log.csv";
}

FILogFile* pLog = NULL;
pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp);

// Get the leading info for this line
strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns());
otherPlayerName = GET_PLAYER(*it).getCivilizationShortDescription();
strBaseString += playerName + " VS. " + otherPlayerName;

strOutBuf.Format("Automatic peace offer: We're badly losing this war!");

strBaseString += strOutBuf;
pLog->Msg(strBaseString);
}
continue;
}
// Let's offer peace if we're in danger of losing a city we originally founded (or a really important city) and they're not at risk of losing a city
else if (bSeriousDangerUs && !bSeriousDangerThem)
{
vMakePeacePlayers.push_back(*it);
if (bLog)
{
CvString strOutBuf;
CvString strBaseString;
CvString playerName;
CvString otherPlayerName;
CvString strLogName;

// Find the name of this civ and city
playerName = m_pPlayer->getCivilizationShortDescription();

// Open the log file
if (GC.getPlayerAndCityAILogSplit())
{
strLogName = "DiplomacyAI_Peace_Log" + playerName + ".csv";
}
else
{
strLogName = "DiplomacyAI_Peace_Log.csv";
}

FILogFile* pLog = NULL;
pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp);

// Get the leading info for this line
strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns());
otherPlayerName = GET_PLAYER(*it).getCivilizationShortDescription();
strBaseString += playerName + " VS. " + otherPlayerName;

strOutBuf.Format("Automatic peace offer: We're in serious danger of losing an important city and they're not in serious danger!");

strBaseString += strOutBuf;
pLog->Msg(strBaseString);
}
continue;
}

// Do not make peace if it would block us from achieving a Domination Victory we're going for (possible with DiploAIOptions.sql)
{
if (IsGoingForWorldConquest() || IsCloseToWorldConquest())
{
SetTreatyWillingToOffer(*it, NO_PEACE_TREATY_TYPE);
SetTreatyWillingToAccept(*it, NO_PEACE_TREATY_TYPE);
if (bLog)
{
CvString strOutBuf;
CvString strBaseString;
CvString playerName;
CvString otherPlayerName;
CvString strLogName;

// Find the name of this civ and city
playerName = m_pPlayer->getCivilizationShortDescription();

// Open the log file
if (GC.getPlayerAndCityAILogSplit())
{
strLogName = "DiplomacyAI_Peace_Log" + playerName + ".csv";
}
else
{
strLogName = "DiplomacyAI_Peace_Log.csv";
}

FILogFile* pLog = NULL;
pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp);

// Get the leading info for this line
strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns());
otherPlayerName = GET_PLAYER(*it).getCivilizationShortDescription();
strBaseString += playerName + " VS. " + otherPlayerName;

strOutBuf.Format("No peace! We'll lose our shot at Domination Victory if we make peace!");

strBaseString += strOutBuf;
pLog->Msg(strBaseString);
}
continue;
}
}

// If this is a phony war and they're not in danger, let's offer peace.
if (bPhonyWar && iTheirDanger == 0)
{
vMakePeacePlayers.push_back(*it);
if (bLog)
{
CvString strOutBuf;
CvString strBaseString;
CvString playerName;
CvString otherPlayerName;
CvString strLogName;

// Find the name of this civ and city
playerName = m_pPlayer->getCivilizationShortDescription();

// Open the log file
if (GC.getPlayerAndCityAILogSplit())
{
strLogName = "DiplomacyAI_Peace_Log" + playerName + ".csv";
}
else
{
strLogName = "DiplomacyAI_Peace_Log.csv";
}

FILogFile* pLog = NULL;
pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp);

// Get the leading info for this line
strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns());
otherPlayerName = GET_PLAYER(*it).getCivilizationShortDescription();
strBaseString += playerName + " VS. " + otherPlayerName;

strOutBuf.Format("Automatic peace offer: This is a phony war and none of their cities are in danger!");

strBaseString += strOutBuf;
pLog->Msg(strBaseString);
}
continue;
}

// Test war progress score: if sufficiently negative, AI will agree to make white peace (at minimum)
// But ignore this if opponent's cities are endangered, unless the war has gone on for too long or we're in serious danger
if (vEnemyCitiesEndangered.size() == 0 || bSeriousDangerUs || bInTerribleShape || iWarDuration >= 40)
{
int iWarProgress = GetWarProgressScore(*it);

if (MOD_BALANCE_VP)
{
iWarProgress -= GetPlayer()->GetUnhappinessFromWarWeariness();

int iHappiness = GetPlayer()->GetExcessHappiness();
if (iHappiness < 50)
{
iWarProgress += (50 - iHappiness) * /*-2*/ GD_INT_GET(WAR_PROGRESS_PER_UNHAPPY);
}
}
else if (GetPlayer()->GetExcessHappiness() < 0)
iWarProgress -= GetPlayer()->GetExcessHappiness() * /*-4*/ GD_INT_GET(WAR_PROGRESS_PER_UNHAPPY);

// Adjust for strategic resource shortages
int iStrategicDeficit = 0;
int iMaxStrategicDeficit = -50;

for (int iResourceLoop = 0; iResourceLoop < GC.getNumResourceInfos(); iResourceLoop++)
{
ResourceTypes eResource = (ResourceTypes) iResourceLoop;
if (eResource == NO_RESOURCE)
continue;

const CvResourceInfo* pkResourceInfo = GC.getResourceInfo(eResource);
if (pkResourceInfo == NULL)
continue;

if (pkResourceInfo->getResourceUsage() != RESOURCEUSAGE_STRATEGIC)
continue;

if (GetPlayer()->getResourceShortageValue(eResource) > 0)
iStrategicDeficit += GetPlayer()->getResourceShortageValue(eResource) * /*-5*/ GD_INT_GET(WAR_PROGRESS_PER_STRATEGIC_DEFICIT);

if (iStrategicDeficit <= iMaxStrategicDeficit)
break;
}

iWarProgress += max(iStrategicDeficit, iMaxStrategicDeficit);

if (iWarProgress <= 0)
{
vMakePeacePlayers.push_back(*it);
if (bLog)
{
CvString strOutBuf;
CvString strBaseString;
CvString playerName;
CvString otherPlayerName;
CvString strLogName;

// Find the name of this civ and city
playerName = m_pPlayer->getCivilizationShortDescription();

// Open the log file
if (GC.getPlayerAndCityAILogSplit())
{
strLogName = "DiplomacyAI_Peace_Log" + playerName + ".csv";
}
else
{
strLogName = "DiplomacyAI_Peace_Log.csv";
}

FILogFile* pLog = NULL;
pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp);

// Get the leading info for this line
strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns());
otherPlayerName = GET_PLAYER(*it).getCivilizationShortDescription();
strBaseString += playerName + " VS. " + otherPlayerName;

strOutBuf.Format("Automatic peace offer: We're making no progress in this war!");

strBaseString += strOutBuf;
pLog->Msg(strBaseString);
}
continue;
}
}

// If not ready for vassalage with a neighbor and the war is nearly won, let's persevere.
if (!bReadyForVassalage && GetWarState(*it) == WAR_STATE_NEARLY_WON && GetPlayer()->GetProximityToPlayer(*it) >= PLAYER_PROXIMITY_CLOSE && iWarDuration < 30 && !bInTerribleShape)
{
SetTreatyWillingToOffer(*it, NO_PEACE_TREATY_TYPE);
SetTreatyWillingToAccept(*it, NO_PEACE_TREATY_TYPE);
if (bLog)
{
CvString strOutBuf;
CvString strBaseString;
CvString playerName;
CvString otherPlayerName;
CvString strLogName;
CvString strNoVassalReason;

// Find the name of this civ and city
playerName = m_pPlayer->getCivilizationShortDescription();

// Open the log file
if (GC.getPlayerAndCityAILogSplit())
{
strLogName = "DiplomacyAI_Peace_Log" + playerName + ".csv";
}
else
{
strLogName = "DiplomacyAI_Peace_Log.csv";
}

FILogFile* pLog = NULL;
pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp);

// Get the leading info for this line
strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns());
otherPlayerName = GET_PLAYER(*it).getCivilizationShortDescription();
strBaseString += playerName + " VS. " + otherPlayerName;

strOutBuf.Format("No peace! We've almost won this war and ");

if (!GET_TEAM(GET_PLAYER(*it).getTeam()).canBecomeVassal(GetTeam()))
strNoVassalReason.Format("we're not able to make them a vassal!");
else if (bCapturedAnyCityFromUs)
strNoVassalReason.Format("they have a nearby city they captured from us!");
else if (IsEndgameAggressiveTo(*it))
strNoVassalReason.Format("they're in danger of winning the game!");
else if (bCapturedAnyCityWeWantToLiberate)
strNoVassalReason.Format("they have a nearby city we want to liberate!");
else if (iWarScore < 75)
strNoVassalReason.Format("war score is less than 75!");
else if (IsUntrustworthy(*it))
strNoVassalReason.Format("we don't trust them to be an obedient vassal!");
else
strNoVassalReason.Format("we still want to capture cities from them!");

strOutBuf += strNoVassalReason;
strBaseString += strOutBuf;
pLog->Msg(strBaseString);
}
continue;
}

bool bWarWeary = false;
if (MOD_BALANCE_VP)
{
int iPercentOfPop = GetPlayer()->GetUnhappinessFromWarWeariness() * 100 / max(1, GetPlayer()->getTotalPopulation());
if (iPercentOfPop >= 20)
bWarWeary = true;
}

// Okay, so no automatic peace. But should we consider peace at all?
bool bConsiderPeace = bReadyForVassalage || bPhonyWar || bSeriousDangerUs || bInTerribleShape || bWarWeary;
bConsiderPeace |= GetCivApproach(*it) > CIV_APPROACH_HOSTILE;
bConsiderPeace |= GetWarState(*it) <= WAR_STATE_TROUBLED;
bConsiderPeace |= iWarDuration >= 30;

// Not worth making peace with. Proceed.
if (!bConsiderPeace)
{
SetTreatyWillingToOffer(*it, NO_PEACE_TREATY_TYPE);
SetTreatyWillingToAccept(*it, NO_PEACE_TREATY_TYPE);
if (bLog)
{
CvString strOutBuf;
CvString strBaseString;
CvString playerName;
CvString otherPlayerName;
CvString strLogName;

// Find the name of this civ and city
playerName = m_pPlayer->getCivilizationShortDescription();

// Open the log file
if (GC.getPlayerAndCityAILogSplit())
{
strLogName = "DiplomacyAI_Peace_Log" + playerName + ".csv";
}
else
{
strLogName = "DiplomacyAI_Peace_Log.csv";
}

FILogFile* pLog = NULL;
pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp);

// Get the leading info for this line
strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns());
otherPlayerName = GET_PLAYER(*it).getCivilizationShortDescription();
strBaseString += playerName + " VS. " + otherPlayerName;

strOutBuf.Format("No peace! We don't have a good enough reason to consider peace yet!");

strBaseString += strOutBuf;
pLog->Msg(strBaseString);
}
continue;
}

// If we've gotten this far, we need to at least consider the merits of peace.
int iPeaceScore = 0;

// War score is of course a significant factor
// If we get a tourism bonus from high warscore or special conquest bonuses, let's not end early!
bool bProlong = GetPlayer()->GetPositiveWarScoreTourismMod() > 0 && GET_PLAYER(GetHighestWarscorePlayer()).getTeam() == GET_PLAYER(*it).getTeam();

if (iWarScore <= 0)
{
iPeaceScore += iWarScore / -2;
bProlong = false;
}
else if (bInTerribleShape || bSeriousDangerUs)
{
iPeaceScore += iWarScore / 3;
bProlong = false;
}
else if (vEnemyCitiesEndangeredByUs.size() > 0)
{
bProlong |= bUABonusesFromCityConquest;
if (!bProlong)
iPeaceScore += iWarScore / 5;
}
else if (!bProlong)
{
iPeaceScore += iWarScore / 5;
}

int iMinimumWarDuration = max(0, /*10*/ GD_INT_GET(WAR_MAJOR_MINIMUM_TURNS));
int iTooLongWarThreshold = max(15, iMinimumWarDuration);
if (bProlong)
{
iTooLongWarThreshold *= 2;
iMinimumWarDuration *= 2;
}

int iDurationPenalty = iWarDuration - iMinimumWarDuration;

// Lack of progress in war increases desire for peace (moreso if far away).
if (iWarDuration > iTooLongWarThreshold)
{
switch (GetPlayer()->GetProximityToPlayer(*it))
{
case PLAYER_PROXIMITY_NEIGHBORS:
iPeaceScore += iDurationPenalty;
break;
case PLAYER_PROXIMITY_CLOSE:
iPeaceScore += (iDurationPenalty * 150) / 100;
break;
case PLAYER_PROXIMITY_FAR:
iPeaceScore += iDurationPenalty * 2;
break;
case NO_PLAYER_PROXIMITY:
case PLAYER_PROXIMITY_DISTANT:
iPeaceScore += iDurationPenalty * 3;
break;
}
}

// War weariness
if (MOD_BALANCE_VP)
{
int iWarWeariness = GetPlayer()->GetUnhappinessFromWarWeariness();
iPeaceScore += iWarWeariness / 4;

if (iWarWeariness > 0 && (bWarWeary || GetPlayer()->IsEmpireUnhappy()))
{
if (GetPlayer()->IsEmpireVeryUnhappy() || (GetPlayer()->IsEmpireUnhappy() && bWarWeary))
iPeaceScore += iWarWeariness / 2;
else
iPeaceScore += iWarWeariness / 3;
}
}

// Danger considerations
int iOurMultiplier = 1;
int iTheirMultiplier = 1;

if (!bInTerribleShape || GetWarState(*it) == WAR_STATE_NEARLY_WON)
{
if (GetMilitaryStrengthComparedToUs(*it) < STRENGTH_AVERAGE)
{
iOurMultiplier++;
}
if (IsEasyTarget(*it))
{
iOurMultiplier++;
}
if (GetPlayer()->GetMilitaryAI()->HavePreferredAttackTarget(*it))
{
iOurMultiplier++;
}
if (GetWarState(*it) == WAR_STATE_NEARLY_WON || GET_PLAYER(*it).IsInTerribleShapeForWar())
{
iOurMultiplier++;
}
}
if (!GET_PLAYER(*it).IsInTerribleShapeForWar())
{
if (GetMilitaryStrengthComparedToUs(*it) > STRENGTH_AVERAGE)
{
iTheirMultiplier++;
}
if (bInTerribleShape)
{
iTheirMultiplier++;
}
if (GET_PLAYER(*it).GetDiplomacyAI()->IsEasyTarget(GetID()))
{
iTheirMultiplier++;
}
if (GetPlayer()->GetMilitaryAI()->IsExposedToEnemy(NULL, *it))
{
iTheirMultiplier++;
}
}

// They're in danger and we're not? Let's hold out longer!
if (iOurDanger == 0 && iTheirDanger > 0)
{
if (iWarDuration < 20 || iOurMultiplier > 2 || bSeriousDangerThem)
{
iPeaceScore -= (5 * iTheirDanger * iOurMultiplier);
}
else
{
iPeaceScore -= (2 * iTheirDanger * iOurMultiplier);
}
}
// We're in danger and they're not? Let's hold out less!
else if (iOurDanger > 0 && iTheirDanger == 0)
{
if (iWarScore <= GetWarscoreThresholdNegative()/2 || iTheirMultiplier > 2 || bSeriousDangerUs)
{
iPeaceScore += (5 * iOurDanger * iTheirMultiplier);
}
else
{
iPeaceScore += (2 * iOurDanger * iTheirMultiplier);
}
}
// We're both in danger - how much?
else
{
iPeaceScore -= (2 * iTheirDanger * iOurMultiplier);
iPeaceScore += (2 * iOurDanger * iTheirMultiplier);
}

// Also consider the overall war state
bool bLosingOverall = GetStateAllWars() == STATE_ALL_WARS_LOSING;

switch (GetWarState(*it))
{
case NO_WAR_STATE_TYPE:
UNREACHABLE(); // Being here would indicate we aren't at a war with this player.
case WAR_STATE_NEARLY_WON:
iPeaceScore -= bReadyForVassalage ? 0 : 10 * iOurMultiplier;
break;
case WAR_STATE_OFFENSIVE:
iPeaceScore -= 5 * iOurMultiplier;
break;
case WAR_STATE_CALM:
iPeaceScore -= bLosingOverall ? -5 * iTheirMultiplier : 2 * iOurMultiplier;
break;
case WAR_STATE_STALEMATE:
case WAR_STATE_TROUBLED:
iPeaceScore += bLosingOverall ? 5 * iTheirMultiplier : 2 * iTheirMultiplier;
break;
case WAR_STATE_DEFENSIVE:
case WAR_STATE_NEARLY_DEFEATED:
iPeaceScore += bLosingOverall ? 10 * iTheirMultiplier : 5 * iTheirMultiplier;
break;
}

// Hold out for longer if we have military support from nearby allies
int iAlliesMod = 0;
if (!bInTerribleShape)
{
for (std::vector<PlayerTypes>::iterator iter = vOurWarAllies.begin(); iter != vOurWarAllies.end(); iter++)
{
if (GET_PLAYER(*iter).GetID() == GetID())
continue;
if (!GET_PLAYER(*iter).isMajorCiv())
continue;
if (GET_PLAYER(*iter).IsInTerribleShapeForWar())
continue;
if (GET_PLAYER(*iter).GetProximityToPlayer(GetID()) < PLAYER_PROXIMITY_CLOSE)
continue;
if (GET_PLAYER(*iter).GetProximityToPlayer(*it) < PLAYER_PROXIMITY_CLOSE)
continue;
if (GET_PLAYER(*iter).GetDiplomacyAI()->GetWarState(*it) <= WAR_STATE_TROUBLED)
continue;

switch (GET_PLAYER(*iter).GetDiplomacyAI()->GetMilitaryStrengthComparedToUs(*it))
{
case NO_STRENGTH_VALUE:
UNREACHABLE(); // Strengths are supposed to have been evaluated by this point.
case STRENGTH_IMMENSE:
case STRENGTH_POWERFUL:
case STRENGTH_STRONG:
break;
case STRENGTH_AVERAGE:
case STRENGTH_POOR:
iAlliesMod -= 5;
break;
case STRENGTH_WEAK:
iAlliesMod -= 10;
break;
case STRENGTH_PATHETIC:
iAlliesMod -= 15;
break;
}
}
// Ongoing coop war? Minimum -10 modifier.
if (iAlliesMod > -10 && GetGlobalCoopWarAgainstState(*it) == COOP_WAR_STATE_ONGOING)
{
iAlliesMod = -10;
}
}
if (iWarScore <= GetWarscoreThresholdNegative() && GetWarState(*it) < WAR_STATE_OFFENSIVE)
{
iAlliesMod /= 2;
}

iPeaceScore += iAlliesMod;

// If we want to conquer them, let's hold out longer.
{
iPeaceScore -= 10;
}

// If they captured one of our key cities, let's hold out for significantly longer.
if (bCapturedKeyCity)
{
iPeaceScore -= 15;
}

// If they're about to win the game, let's hold out for a lot longer.
if (IsEndgameAggressiveTo(*it))
{
iPeaceScore -= 20;
}

bool bAztecException = false;

if (iPeaceScore > 0)
{
// If we're going for world conquest, we want to fight our wars until we get their capital or can vassalize them
// We should also be more reluctant to make peace if they've captured cities from us, or cities that we want to liberate
// However, do not factor this in when losing
if (!bInTerribleShape)
{
if (iWarScore > 0 || (iWarScore > GetWarscoreThresholdNegative()/2 && GetRawTargetValue(*it) >= TARGET_VALUE_AVERAGE && GetWarState(*it) > WAR_STATE_TROUBLED))
{
if (iWarScore < WARSCORE_THRESHOLD_POSITIVE && GetPlayer()->GetPlayerTraits()->GetGoldenAgeFromVictory() > 0)
{
bAztecException = true;
iPeaceScore /= 2;
}
else if (bCapturedAnyCityFromUs)
{
if (iWarScore > (iWarDuration / 2))
iPeaceScore /= 2;
else
{
iPeaceScore *= 75;
iPeaceScore /= 100;
}
}
else if (bCapturedAnyCityWeWantToLiberate)
{
if ((bDiplomatic && iWarScore > 0) || iWarScore > iWarDuration)
{
if (bDiplomatic && iWarScore > iWarDuration)
{
iPeaceScore /= 2;
}
else
{
iPeaceScore *= 75;
iPeaceScore /= 100;
}
}
}
else if (bWorldConquest)
{
if (bReadyForVassalage || (GET_PLAYER(*it).GetCapitalConqueror() != NO_PLAYER && GET_PLAYER(*it).GetNumCapitalCities() <= 0))
{
// If they're economically weak or a bad target, boost peace willingness
if (GetEconomicStrengthComparedToUs(*it) <= STRENGTH_WEAK || GetRawTargetValue(*it) <= TARGET_VALUE_BAD)
iPeaceScore *= 2;
else if (GetTargetValue(*it) == TARGET_VALUE_DIFFICULT)
{
iPeaceScore *= 3;
iPeaceScore /= 2;
}
}
else if (GET_PLAYER(*it).GetCapitalConqueror() == NO_PLAYER || GET_PLAYER(*it).GetNumCapitalCities() > 0)
{
iPeaceScore *= 75;
iPeaceScore /= 100;
}
}
}
}
// Overall state is bad and we're doing great in this war, or at least haven't lost any cities? Let's peace out!
else if (!bCapturedAnyCityFromUs || bReadyForVassalage || GetWarState(*it) == WAR_STATE_NEARLY_WON)
{
iPeaceScore *= 2;

// Both? Even more likely to make peace!
if (!bCapturedAnyCityFromUs && (bReadyForVassalage || GetWarState(*it) == WAR_STATE_NEARLY_WON))
iPeaceScore *= 2;
}

// Modify based on leader flavors
// High Meanness leaders will fight to the bitter end when losing, high Diplo Balance leaders like to return to status quo when winning
if (iWarScore > 0)
{
}
else if (iWarScore < 0)
{
}
}

// Must be high enough to return a true desire for peace
int iWarCount = GetPlayer()->CountNumDangerousMajorsAtWarWith(true, false) - 1;
if (iWarCount <= 0)
iWarCount = 0;

bool bWantedPeaceLastTime = IsWantsPeaceWithPlayer(*it) && !bAztecException; // if we were willing to make peace last update, add some leeway here to prevent flip-flopping
int iThreshold = bWantedPeaceLastTime ? /*17*/ GD_INT_GET(REQUEST_PEACE_LEEWAY_THRESHOLD) : /*20*/ GD_INT_GET(REQUEST_PEACE_TURN_THRESHOLD);
int iThresholdReductionPerOtherWar = bWantedPeaceLastTime ? /*3*/ GD_INT_GET(REQUEST_PEACE_LEEWAY_THRESHOLD_REDUCTION_PER_WAR) : /*2*/ GD_INT_GET(REQUEST_PEACE_THRESHOLD_REDUCTION_PER_WAR);

iThreshold = max(iThreshold - max(iThresholdReductionPerOtherWar * iWarCount, 0), 1);

if (iPeaceScore >= iThreshold)
vMakePeacePlayers.push_back(*it);
else
{
SetTreatyWillingToOffer(*it, NO_PEACE_TREATY_TYPE);
SetTreatyWillingToAccept(*it, NO_PEACE_TREATY_TYPE);
}

if (bLog)
{
CvString strOutBuf;
CvString strBaseString;
CvString playerName;
CvString otherPlayerName;
CvString strLogName;

// Find the name of this civ and city
playerName = m_pPlayer->getCivilizationShortDescription();

// Open the log file
if (GC.getPlayerAndCityAILogSplit())
{
strLogName = "DiplomacyAI_Peace_Log" + playerName + ".csv";
}
else
{
strLogName = "DiplomacyAI_Peace_Log.csv";
}

FILogFile* pLog = NULL;
pLog = LOGFILEMGR.GetLog(strLogName, FILogFile::kDontTimeStamp);

// Get the leading info for this line
strBaseString.Format("%03d, ", GC.getGame().getElapsedGameTurns());
otherPlayerName = GET_PLAYER(*it).getCivilizationShortDescription();
strBaseString += playerName + " VS. " + otherPlayerName;

if (iPeaceScore >= iThreshold)
strOutBuf.Format("Willing to make peace, Score for Peace: %d, Required Score: %d", iPeaceScore, iThreshold);
else
strOutBuf.Format("Not willing to make peace, Score for Peace: %d, Required Score: %d", iPeaceScore, iThreshold);

strBaseString += strOutBuf;
pLog->Msg(strBaseString);
}
}

// STEP 4: Decide WHAT we're willing to accept/offer in exchange for peace.
for (std::vector<PlayerTypes>::iterator it = vMakePeacePlayers.begin(); it != vMakePeacePlayers.end(); it++)
{
PeaceTreatyTypes eTreatyWillingToOffer = PEACE_TREATY_WHITE_PEACE;
PeaceTreatyTypes eTreatyWillingToAccept = PEACE_TREATY_WHITE_PEACE;
int iWillingToOfferScore = 0;
int iWillingToAcceptScore = 0;

// What we're willing to offer/accept in a peace treaty.
int iWarScore = GetWarScore(*it);

// Negative Warscore? Offer more.
if (iWarScore < 0)
{
iWillingToOfferScore -= iWarScore;
}
// Positive Warscore? Accept more.
else
{
iWillingToAcceptScore += iWarScore;
}

// War Weary? We're more willing to make peace.
if (MOD_BALANCE_VP)
{
if (iWarScore <= 0)
{
iWillingToOfferScore += GetPlayer()->GetUnhappinessFromWarWeariness();
}
else
{
iWillingToAcceptScore -= GetPlayer()->GetUnhappinessFromWarWeariness();
}
}

// Do the final assessment
if (iWillingToOfferScore >= /*100*/ GD_INT_GET(PEACE_WILLINGNESS_OFFER_THRESHOLD_UN_SURRENDER))
eTreatyWillingToOffer = PEACE_TREATY_UNCONDITIONAL_SURRENDER;
else if (iWillingToOfferScore >= /*90*/ GD_INT_GET(PEACE_WILLINGNESS_OFFER_THRESHOLD_CAPITULATION))
eTreatyWillingToOffer = PEACE_TREATY_CAPITULATION;
else if (iWillingToOfferScore >= /*80*/ GD_INT_GET(PEACE_WILLINGNESS_OFFER_THRESHOLD_CESSION))
eTreatyWillingToOffer = PEACE_TREATY_CESSION;
else if (iWillingToOfferScore >= /*70*/ GD_INT_GET(PEACE_WILLINGNESS_OFFER_THRESHOLD_SURRENDER))
eTreatyWillingToOffer = PEACE_TREATY_SURRENDER;
else if (iWillingToOfferScore >= /*60*/ GD_INT_GET(PEACE_WILLINGNESS_OFFER_THRESHOLD_SUBMISSION))
eTreatyWillingToOffer = PEACE_TREATY_SUBMISSION;
else if (iWillingToOfferScore >= /*40*/ GD_INT_GET(PEACE_WILLINGNESS_OFFER_THRESHOLD_BACKDOWN))
eTreatyWillingToOffer = PEACE_TREATY_BACKDOWN;
else if (iWillingToOfferScore >= /*30*/ GD_INT_GET(PEACE_WILLINGNESS_OFFER_THRESHOLD_SETTLEMENT))
eTreatyWillingToOffer = PEACE_TREATY_SETTLEMENT;
else if (iWillingToOfferScore >= /*15*/ GD_INT_GET(PEACE_WILLINGNESS_OFFER_THRESHOLD_ARMISTICE))
eTreatyWillingToOffer = PEACE_TREATY_ARMISTICE;

// Do the final assessment
if (iWillingToAcceptScore >= /*100*/ GD_INT_GET(PEACE_WILLINGNESS_ACCEPT_THRESHOLD_UN_SURRENDER))
eTreatyWillingToAccept = PEACE_TREATY_UNCONDITIONAL_SURRENDER;
else if (iWillingToAcceptScore >= /*90*/ GD_INT_GET(PEACE_WILLINGNESS_ACCEPT_THRESHOLD_CAPITULATION))
eTreatyWillingToAccept = PEACE_TREATY_CAPITULATION;
else if (iWillingToAcceptScore >= /*80*/ GD_INT_GET(PEACE_WILLINGNESS_ACCEPT_THRESHOLD_CESSION))
eTreatyWillingToAccept = PEACE_TREATY_CESSION;
else if (iWillingToAcceptScore >= /*70*/ GD_INT_GET(PEACE_WILLINGNESS_ACCEPT_THRESHOLD_SURRENDER))
eTreatyWillingToAccept = PEACE_TREATY_SURRENDER;
else if (iWillingToAcceptScore >= /*60*/ GD_INT_GET(PEACE_WILLINGNESS_ACCEPT_THRESHOLD_SUBMISSION))
eTreatyWillingToAccept = PEACE_TREATY_SUBMISSION;
else if (iWillingToAcceptScore >= /*40*/ GD_INT_GET(PEACE_WILLINGNESS_ACCEPT_THRESHOLD_BACKDOWN))
eTreatyWillingToAccept = PEACE_TREATY_BACKDOWN;
else if (iWillingToAcceptScore >= /*30*/ GD_INT_GET(PEACE_WILLINGNESS_ACCEPT_THRESHOLD_SETTLEMENT))
eTreatyWillingToAccept = PEACE_TREATY_SETTLEMENT;
else if (iWillingToAcceptScore >= /*15*/ GD_INT_GET(PEACE_WILLINGNESS_ACCEPT_THRESHOLD_ARMISTICE))
eTreatyWillingToAccept = PEACE_TREATY_ARMISTICE;

// If we're in a critical state, always accept a white peace.
if (bCriticalState)
{
if (eTreatyWillingToOffer <= PEACE_TREATY_WHITE_PEACE)
eTreatyWillingToOffer = PEACE_TREATY_WHITE_PEACE;

eTreatyWillingToAccept = PEACE_TREATY_WHITE_PEACE;
}

SetTreatyWillingToOffer(*it, eTreatyWillingToOffer);
SetTreatyWillingToAccept(*it, eTreatyWillingToAccept);
}
}``````

Reasons why peace might be blocked:
Code:
``````static void FmtPeaceBlockReasonLogStr(CvString& str, PlayerTypes eThisPlayer, PlayerTypes eAtWarPlayer, PeaceBlockReasons eReason)
{
switch (eReason)
{
case NO_PEACE_BLOCK_REASON:
UNREACHABLE(); // Peace is expected to be blocked.
case PEACE_BLOCK_REASON_ALWAYS_WAR:
str.Format("We are always at war with this player!");
break;
case PEACE_BLOCK_REASON_VASSALAGE:
str.Format("One of us is a vassal!");
break;
case PEACE_BLOCK_REASON_TOO_SOON:
str.Format("Too early to make peace!");
break;
case PEACE_BLOCK_REASON_CITY_JUST_CAPTURED:
str.Format("Our city was just captured and we have units near the enemy's cities!");
break;
case PEACE_BLOCK_REASON_AT_WAR_WITH_ALLY:
str.Format("They're at war with our ally!");
break;
case PEACE_BLOCK_REASON_WAR_DEAL:
str.Format("Locked into coop/3rd party war for %d more turns!", GET_TEAM(GET_PLAYER(eThisPlayer).getTeam()).GetNumTurnsLockedIntoWar(GET_PLAYER(eAtWarPlayer).getTeam()));
break;
case PEACE_BLOCK_REASON_NO_ENEMY_CAPITAL:
str.Format("Other player has no cities so we lose nothing from maintaining the war!");
break;
case PEACE_BLOCK_REASON_COOP_WAR_AGAINST_DEFENSIVE_PACT:
str.Format("We're planning a coop war against someone they have a Defensive Pact with! Making peace would cancel the coop war!");
break;
case PEACE_BLOCK_REASON_SCENARIO:
str.Format("Cannot change war/peace status due to special scenario rule!");
break;
}
}``````

There is special logic on whether or not the AI is willing to capitulate if they have team members. I'll include it if you're curious.
Code:
``````    for (size_t i=0; i<vOurTeam.size(); i++)
{
if (!GET_PLAYER(vOurTeam[i]).isAlive() || !GET_PLAYER(vOurTeam[i]).isMajorCiv() || GET_PLAYER(vOurTeam[i]).getNumCities() <= 0)
continue;

bool bYes = false;

for (size_t j=0; j<vTheirTeam.size(); j++)
{
if (!GET_PLAYER(vTheirTeam[j]).isAlive() || !GET_PLAYER(vTheirTeam[j]).isMajorCiv() || GET_PLAYER(vTheirTeam[j]).getNumCities() <= 0)
continue;

// If we're at war, this is capitulation - otherwise, it's voluntary vassalage
// If we agree to become the vassal of any of their team members, then we agree to become the vassal of all of them
if (IsAtWar(ePlayer))
{
if (GET_PLAYER(vOurTeam[i]).GetDiplomacyAI()->IsCapitulationAcceptable(vTheirTeam[j]))
{
bYes = true;
iNumTeamMembersInFavor++;
break;
}
}
else
{
if (GET_PLAYER(vOurTeam[i]).GetDiplomacyAI()->IsVoluntaryVassalageAcceptable(vTheirTeam[j]))
{
bYes = true;
iNumTeamMembersInFavor++;
break;
}
}
}

if (!bYes)
iNumTeamMembersAgainst++;
}

// More team members must agree than refuse
return iNumTeamMembersInFavor > 0 && iNumTeamMembersInFavor > iNumTeamMembersAgainst;``````

Thank you very much for the answer and sharing the relevant code parts. There is quite a lot of cases and as I have not coded anything for CV5 so I am able to get only a bit of insight into it...

I think this really should be solved for optimal gameplay. Lets start with my usecase for simplicity - that no AI is in a team (only humans are), which means no Team vs Team computation is needed, only single AI vs Team. So let me start with an overview:
1) The AI should compute its bConsiderPeace against all players in that team. If it is false at least for one player, AI will not consider peace with that team.
2) The AI should compute its
iPeaceScore against all players in that team (but proximity is taken into action which will need to be adjusted later...).
3) The AI should make an aggregation of these iPeaceScore. I would say weighted average taking proximity (neighbors more weight) into consideration would be ok for a start. The value is then compared to the iThreshold
and if (and only if) it is sufficient then AI will be willing to sign peace with any player of that team.
4) The AI should compute
iWillingToOfferScore and iWillingToAcceptScore and make aggregation of these values the same way as in previous step and use the same values for every player in the same team.

Is this ok for a start? Should a Congress proposal be made, or is this considered a AI improve/bugfix? And if possible... is there anyone willing to implement it thoroughly? (I would not dare, I do not understand the code enough)

Make a congress proposal

Thank you very much for the answer and sharing the relevant code parts. There is quite a lot of cases and as I have not coded anything for CV5 so I am able to get only a bit of insight into it...

I think this really should be solved for optimal gameplay. Lets start with my usecase for simplicity - that no AI is in a team (only humans are), which means no Team vs Team computation is needed, only single AI vs Team. So let me start with an overview:
1) The AI should compute its bConsiderPeace against all players in that team. If it is false at least for one player, AI will not consider peace with that team.
2) The AI should compute its
iPeaceScore against all players in that team (but proximity is taken into action which will need to be adjusted later...).
3) The AI should make an aggregation of these iPeaceScore. I would say weighted average taking proximity (neighbors more weight) into consideration would be ok for a start. The value is then compared to the iThreshold
and if (and only if) it is sufficient then AI will be willing to sign peace with any player of that team.
4) The AI should compute
iWillingToOfferScore and iWillingToAcceptScore and make aggregation of these values the same way as in previous step and use the same values for every player in the same team.

Is this ok for a start? Should a Congress proposal be made, or is this considered a AI improve/bugfix? And if possible... is there anyone willing to implement it thoroughly? (I would not dare, I do not understand the code enough)
War duration (turns since war started / city captured) is stored separately for each player, which would have effects if some players on a team died / were resurrected mid conflict, but it'd be easy enough to use the highest value out of all team members.

Otherwise, feel free to make a proposal, although note that you'd have to specify how the weighted average calculations work.

Proximity is generally measured in the DLL using NEIGHBORS, CLOSE, FAR or DISTANT thresholds. It can be calculated in other ways, such as based on the shortest distance between any of player A's cities to any of player B's cities, though this is more computationally expensive.

I thought pure AI changes didn't belong in a proposal?

I thought pure AI changes didn't belong in a proposal?
The proposal must be about game balance or other mechanics visible to the player. Proposals about purely internal changes such as performance improvements, modmod compatibility or database restructuring should not be made here – open an issue or submit a pull request on GitHub instead.
AI improvements can be made in a proposal, they just don't have to be. The output of an AI behaving differently in the same situation is visible to the player.

Replies
114
Views
10K
Replies
2
Views
528
Replies
22
Views
2K
Replies
24
Views
2K
Replies
42
Views
3K