Ok, as this was bugging me for quite a while, I decided to give a look to the code ... and was surprised
First I decided to check CvPlayerAI and look for the name of the function that the vassal trade called:
Code:
case TRADE_VASSAL:
if (!bIgnoreAnnual)
{
iValue += GET_TEAM(getTeam()).AI_vassalTradeVal(GET_PLAYER(ePlayer).getTeam());
}
break;
Fine so far ... so i go the CvTeamAI and see what this function calls:
Code:
int CvTeamAI::AI_vassalTradeVal(TeamTypes eTeam) const
{
FAssertMsg(eTeam != getID(), "shouldn't call this function on ourselves");
return AI_surrenderTradeVal(eTeam);
}
And it calls the code that is also used for calculating capitulation
To be honest, I should know better ,since I actually had read CvTeamAI::AI_surrenderTradeVal quite a while ago to suggest changes to the better AI mod
Anyway, here is the full code, for those lazy to check the Dll folder
Code:
DenialTypes CvTeamAI::AI_surrenderTrade(TeamTypes eTeam, int iPowerMultiplier) const
{
PROFILE_FUNC();
FAssertMsg(eTeam != getID(), "shouldn't call this function on ourselves");
CvTeam& kMasterTeam = GET_TEAM(eTeam);
for (int iLoopTeam = 0; iLoopTeam < MAX_TEAMS; iLoopTeam++)
{
CvTeam& kLoopTeam = GET_TEAM((TeamTypes)iLoopTeam);
if (kLoopTeam.isAlive() && iLoopTeam != getID() && iLoopTeam != kMasterTeam.getID())
{
if (kLoopTeam.isAtWar(kMasterTeam.getID()) && !kLoopTeam.isAtWar(getID()))
{
if (isForcePeace((TeamTypes)iLoopTeam) || !canChangeWarPeace((TeamTypes)iLoopTeam))
{
if (!kLoopTeam.isAVassal())
{
return DENIAL_WAR_NOT_POSSIBLE_US;
}
}
}
else if (!kLoopTeam.isAtWar(kMasterTeam.getID()) && kLoopTeam.isAtWar(getID()))
{
if (!canChangeWarPeace((TeamTypes)iLoopTeam))
{
if (!kLoopTeam.isAVassal())
{
return DENIAL_PEACE_NOT_POSSIBLE_US;
}
}
}
}
}
if (isHuman())
{
return NO_DENIAL;
}
int iAttitudeModifier = 0;
if (!GET_TEAM(eTeam).isParent(getID()))
{
int iPersonalityModifier = 0;
int iMembers = 0;
for (int iI = 0; iI < MAX_PLAYERS; iI++)
{
if (GET_PLAYER((PlayerTypes)iI).isAlive())
{
if (GET_PLAYER((PlayerTypes)iI).getTeam() == getID())
{
iPersonalityModifier += GC.getLeaderHeadInfo(GET_PLAYER((PlayerTypes)iI).getPersonalityType()).getVassalPowerModifier();
++iMembers;
}
}
}
int iTotalPower = GC.getGameINLINE().countTotalCivPower();
int iAveragePower = iTotalPower / std::max(1, GC.getGameINLINE().countCivTeamsAlive());
int iMasterPower = GET_TEAM(eTeam).getPower(false);
int iVassalPower = (getPower(true) * (iPowerMultiplier + iPersonalityModifier / std::max(1, iMembers))) / 100;
if (isAtWar(eTeam))
{
int iTheirSuccess = std::max(10, GET_TEAM(eTeam).AI_getWarSuccess(getID()));
int iOurSuccess = std::max(10, AI_getWarSuccess(eTeam));
int iOthersBestSuccess = 0;
for (int iTeam = 0; iTeam < MAX_CIV_TEAMS; ++iTeam)
{
if (iTeam != eTeam && iTeam != getID())
{
CvTeam& kLoopTeam = GET_TEAM((TeamTypes)iTeam);
if (kLoopTeam.isAlive() && kLoopTeam.isAtWar(getID()))
{
int iSuccess = kLoopTeam.AI_getWarSuccess(getID());
if (iSuccess > iOthersBestSuccess)
{
iOthersBestSuccess = iSuccess;
}
}
}
}
// Discourage capitulation to a team that has not done the most damage
if (iTheirSuccess < iOthersBestSuccess)
{
iOurSuccess += iOthersBestSuccess - iTheirSuccess;
}
iMasterPower = (2 * iMasterPower * iTheirSuccess) / (iTheirSuccess + iOurSuccess);
if (AI_getWorstEnemy() == eTeam)
{
iMasterPower *= 3;
iMasterPower /= 4;
}
}
else
{
if (!GET_TEAM(eTeam).AI_isLandTarget(getID()))
{
iMasterPower /= 2;
}
}
for (int iLoopTeam = 0; iLoopTeam < MAX_CIV_TEAMS; iLoopTeam++)
{
if (iLoopTeam != getID())
{
CvTeamAI& kLoopTeam = GET_TEAM((TeamTypes)iLoopTeam);
if (kLoopTeam.isAlive())
{
if (kLoopTeam.AI_isLandTarget(getID()))
{
if (iLoopTeam != eTeam)
{
if (kLoopTeam.getPower(true) > getPower(true))
{
if (kLoopTeam.isAtWar(eTeam) && !kLoopTeam.isAtWar(getID()))
{
return DENIAL_POWER_YOUR_ENEMIES;
}
iAveragePower = (2 * iAveragePower * kLoopTeam.getPower(true)) / std::max(1, kLoopTeam.getPower(true) + getPower(true));
iAttitudeModifier += (3 * kLoopTeam.getPower(true)) / std::max(1, getPower(true)) - 2;
}
if (!kLoopTeam.isAtWar(eTeam) && kLoopTeam.isAtWar(getID()))
{
iAveragePower = (iAveragePower * (getPower(true) + GET_TEAM(eTeam).getPower(false))) / std::max(1, getPower(true));
}
}
}
if (!atWar(getID(), eTeam))
{
if (kLoopTeam.isAtWar(eTeam) && !kLoopTeam.isAtWar(getID()))
{
DenialTypes eDenial = AI_declareWarTrade((TeamTypes)iLoopTeam, eTeam, false);
if (eDenial != NO_DENIAL)
{
return eDenial;
}
}
}
}
}
}
if (!isVassal(eTeam) && canVassalRevolt(eTeam))
{
return DENIAL_POWER_US;
}
if (iVassalPower > iAveragePower || 3 * iVassalPower > 2 * iMasterPower)
{
return DENIAL_POWER_US;
}
for (int i = 0; i < GC.getNumVictoryInfos(); i++)
{
bool bPopulationThreat = true;
if (GC.getGameINLINE().getAdjustedPopulationPercent((VictoryTypes)i) > 0)
{
bPopulationThreat = false;
int iThreshold = GC.getGameINLINE().getTotalPopulation() * GC.getGameINLINE().getAdjustedPopulationPercent((VictoryTypes)i);
if (400 * getTotalPopulation(!isAVassal()) > 3 * iThreshold)
{
return DENIAL_VICTORY;
}
if (!atWar(getID(), eTeam))
{
if (400 * (getTotalPopulation(isAVassal()) + GET_TEAM(eTeam).getTotalPopulation()) > 3 * iThreshold)
{
bPopulationThreat = true;
}
}
}
bool bLandThreat = true;
if (GC.getGameINLINE().getAdjustedLandPercent((VictoryTypes)i) > 0)
{
bLandThreat = false;
int iThreshold = GC.getMapINLINE().getLandPlots() * GC.getGameINLINE().getAdjustedLandPercent((VictoryTypes)i);
if (400 * getTotalLand(!isAVassal()) > 3 * iThreshold)
{
return DENIAL_VICTORY;
}
if (!atWar(getID(), eTeam))
{
if (400 * (getTotalLand(isAVassal()) + GET_TEAM(eTeam).getTotalLand()) > 3 * iThreshold)
{
bLandThreat = true;
}
}
}
if (GC.getGameINLINE().getAdjustedPopulationPercent((VictoryTypes)i) > 0 || GC.getGameINLINE().getAdjustedLandPercent((VictoryTypes)i) > 0)
{
if (bLandThreat && bPopulationThreat)
{
return DENIAL_POWER_YOU;
}
}
}
}
if (!isAtWar(eTeam))
{
if (!GET_TEAM(eTeam).isParent(getID()))
{
if (AI_getWorstEnemy() == eTeam)
{
return DENIAL_WORST_ENEMY;
}
if (!AI_hasCitiesInPrimaryArea(eTeam) && AI_calculateAdjacentLandPlots(eTeam) == 0)
{
return DENIAL_TOO_FAR;
}
}
AttitudeTypes eAttitude = AI_getAttitude(eTeam, false);
AttitudeTypes eModifiedAttitude = CvPlayerAI::AI_getAttitudeFromValue(AI_getAttitudeVal(eTeam, false) + iAttitudeModifier);
for (int iI = 0; iI < MAX_PLAYERS; iI++)
{
if (GET_PLAYER((PlayerTypes)iI).isAlive())
{
if (GET_PLAYER((PlayerTypes)iI).getTeam() == getID())
{
if (eAttitude <= ATTITUDE_FURIOUS)
{
return DENIAL_ATTITUDE;
}
if (eModifiedAttitude <= GC.getLeaderHeadInfo(GET_PLAYER((PlayerTypes)iI).getPersonalityType()).getVassalRefuseAttitudeThreshold())
{
return DENIAL_ATTITUDE;
}
}
}
}
}
else
{
if (AI_getWarSuccess(eTeam) + 4 * GC.getDefineINT("WAR_SUCCESS_CITY_CAPTURING") > GET_TEAM(eTeam).AI_getWarSuccess(getID()))
{
return DENIAL_JOKING;
}
}
return NO_DENIAL;
}
Ok, it is already 11.30 PM around here , so I'll not make a extensive reading of the code, but some stuff jumped out to my sight as I (re)read the code ( all of this is IIUC , so beware
):
- They will not propose or acept a peaceful vassaling if they would be forced to a war with someone they have a enforced peace deal with. Duh, but with so much things taking precedence before peace deals (say, AP stuff ), this is actually good to know.
- As far as I understand, the average rule still applies in peacevassal ... in other words, if the AI has more than the average power of the alive civs ( vassals included ), they will not acept or propose peacevassaling
- They will also not acept or propose peacevassaling if they have more than 2/3 of the possible master power. This power gauge includes the usual modifications due to the existance of other vassals of the possible master...
- The power gauge is cut in half if the possible master has no presence in the landmass where the possible vassal has his cap ( well, it is a little more esoterical than that ... the infamous isLandTarget is called for this
)
- They will not peacevassal if the master is warring someone more powerful than them ( the dreaded DENIAL_POWER_YOUR_ENEMIES
)
- There are 2 DENIAL_VICTORY below this that I do not undertand very well, but they seem to me similar to the other DENIAL_VICTORY around ( aka "We want to win the game, thank you" red text ), so it makes sense : a AI that considers it self in the path to a win ( in spite of this being highly schematic in BtS ) will not consider itself to become a vassal peacefully
- They will also not acept peacevassaling if the possible master has either the pop or the land area for a dom win ( atleast I think you can't get there without hitting the other conditions in that loop ... can someone check ? )
- The possible master will not acept their worst enemy as a peacevassal
- They will also not acept a peacevassal that is not located in the master cap landmass and that has no border with him or with other vassal of the master
- No peacevassaling if the master is furious with the possible peacevassal
- Also no peace vassaling if the master has less than the VassalRefuseAttitudeThreshold ( check CivIVLeaderHeadInfos.XML for the leader in question ) to the possible vassal (atleast I think it is the master one it is called ... getting sleepy :/ )
I just want to point 2 things:
- There is no evaluation of the vassal value to the master
In other terms, and unlike it is widely said in here ( including by me
), this is not a deal where the peacevassal sells it self. It is simply a bunch of power , geographical and diplochecks ( like in capitulation ). If the proposed peacevassal passes all those checks, the deal is done.
- There is no direct "fear factor". The AI will not peacevassal just because they are in war and need protection ... it is the consequences of the war that might push the AI to a peacevassal deal with a third party ( most likely the reduced power
)
P.S Please , can someone check the conclusions I made here? I'm sleepy and I would not be surprised if I understood something in the wrong way, especially the diplochecks.
EDIT:
Of course, for a sense of perspective, keep in mind we're talking about the same design team that thinks desert fairy magic (DFM) was a good idea to include in the base game, even though it doesn't change the relative positions of the civs one bit
I prefer the term "Evil Sand Fairy"