As a French person I feel like it's my duty to explain strikes to you. - AdrienIer

Create an account  

 
Curious Civplayer - Mysteries of the DLL

How does one decrease war weariness for a team?

This will be shorter then the last one. We look at CvTeam and there into doWarWeariness, which is called during the turn roll:

Code:
void CvTeam::doWarWeariness()
{
    int iI;

    for (iI = 0; iI < MAX_TEAMS; iI++)
    {
        if (getWarWeariness((TeamTypes)iI) > 0)
        {
            changeWarWeariness(((TeamTypes)iI), 100 * GC.getDefineINT("WW_DECAY_RATE"));

            if (!(GET_TEAM((TeamTypes)iI).isAlive()) || !isAtWar((TeamTypes)iI) || GC.getGameINLINE().isOption(GAMEOPTION_ALWAYS_WAR) || GC.getGameINLINE().isOption(GAMEOPTION_NO_CHANGING_WAR_PEACE))
            {
                setWarWeariness(((TeamTypes)iI), ((getWarWeariness((TeamTypes)iI) * GC.getDefineINT("WW_DECAY_PEACE_PERCENT")) / 100));
            }
        }
    }
}

We iterate across all teams and check if we have war weariness towards that team. If there is war weariness we first call changeWarWeariness with 100 * WW_DECAY_RATE (-1). Or in short the war weariness toward every single team decreases by 100 every turn.

Next we check:

- If that team is not alive or
- if we are not at war with that team or
- if the game option "Always war" is active or
- if the game option "Permanent war or peace" is active

if any of those is true, we set the war weariness to

current war weariness * WW_DECAY_PEACE_PERCENT (99 for BtS, 90 for CtH and RtR) / 100.

And that's it. Let's take some examples

Example a)

War Weariness towards A = 280
War against A

Total War Weariness = 280

War Weariness towards A next turn = 280 - 100 = 180

Total War Weariness next turn 180


Example b)

War Weariness towards A = 280
War Weariness towards B = 400
War Weariness towards C = 2800
War against A

Total War Weariness = 3480


War Weariness towards A next turn = 280 - 100 = 180
War Weariness towards B next turn = (400 - 100) * 99 / 100 = 297
War Weariness towards C next turn = (2800 - 100) * 99 / 100 = 2673

Total war weariness next turn 3150

War Weariness towards A next turn for CtH and RtR = 280 - 100 = 180
War Weariness towards B next turn for CtH and RtR = (400 - 100) * 90 / 100 = 270
War Weariness towards C next turn for CtH and RtR = (2800 - 100) * 90 / 100 = 2430

Total war weariness next turn for CtH and RtR 2880

Summary

Every turn the war weariness towards each player decreases by 100. Afterward if the player is dead, you are at peace or game options "Always war" or "Permanent war and peace" are active your war weariness towards that player is set to:

current war weariness * 99 for BtS or 90 for CtH and RtR / 100.
Mods: RtR    CtH

Pitboss: PB39, PB40PB52, PB59 Useful Collections: Pickmethods, Mapmaking, Curious Civplayer

Buy me a coffee
Reply

How is the teams war weariness converted to the players war weariness?

This is handled in CvPlayer and there the method updateWarWearinessPercentAnger. But before we look into that method let's briefly discuss, where this method is called:

- During the turn roll
- For both war parties, when you declare war
- For both war parties, when you sign peace

Now let's look at the method:

Code:
void CvPlayer::updateWarWearinessPercentAnger()
{
    int iNewWarWearinessPercentAnger;
    int iI;

    iNewWarWearinessPercentAnger = 0;

    if (!isBarbarian() && !isMinorCiv())
    {
        for (iI = 0; iI < MAX_CIV_TEAMS; iI++)
        {
            CvTeam& kTeam = GET_TEAM((TeamTypes)iI);
            if (kTeam.isAlive() && !kTeam.isMinorCiv())
            {
                if (kTeam.isAtWar(getTeam()))
                {
                    iNewWarWearinessPercentAnger += (GET_TEAM(getTeam()).getWarWeariness((TeamTypes)iI) * std::max(0, 100 + kTeam.getEnemyWarWearinessModifier())) / 10000;
                }
            }
        }
    }

    iNewWarWearinessPercentAnger = getModifiedWarWearinessPercentAnger(iNewWarWearinessPercentAnger);

    if (getWarWearinessPercentAnger() != iNewWarWearinessPercentAnger)
    {
        m_iWarWearinessPercentAnger = iNewWarWearinessPercentAnger;

        ...
    }
}

First we check that the player is not the barbarian or a minor civ. After that we iterate across all teams. If all these conditions apply:

- the team is alive
- the team is not a minor civ
- we are at war with that team

we then add the following towards iNewWarWearinessPercentAnger

(Our teams war weariness towards that team * (100 + if enemy has Statue of Zeus = 100 otherwise 0)) / 10000

We also ensure that (100 + if enemy has Statue of Zeus = 100 otherwise 0) is never negative. In that case it is 0.

After we did this we have the total war weariness for this player towards each other player. In the next step the war weariness is modifed during the call to getModifiedWarWearinessPercentAnger. After that if the war weariness has changed, we set this iNewWarWearinessPercentAnger as the new warweariness for the player (m_iWarWearinessPercentAnger).

It's worth to take a look at getModifiedWarWearinessPercentAnger.

Code:
int CvPlayer::getModifiedWarWearinessPercentAnger(int iWarWearinessPercentAnger) const         
{
    iWarWearinessPercentAnger *= GC.getDefineINT("BASE_WAR_WEARINESS_MULTIPLIER");

    if (GC.getGameINLINE().isOption(GAMEOPTION_ALWAYS_WAR) || GC.getGameINLINE().isOption(GAMEOPTION_NO_CHANGING_WAR_PEACE))
    {
        iWarWearinessPercentAnger *= std::max(0, (GC.getDefineINT("FORCED_WAR_WAR_WEARINESS_MODIFIER") + 100));
        iWarWearinessPercentAnger /= 100;
    }

    if (GC.getGameINLINE().isGameMultiPlayer())
    {
        iWarWearinessPercentAnger *= std::max(0, (GC.getDefineINT("MULTIPLAYER_WAR_WEARINESS_MODIFIER") + 100));
        iWarWearinessPercentAnger /= 100;
    }

    iWarWearinessPercentAnger *= std::max(0, (GC.getWorldInfo(GC.getMapINLINE().getWorldSize()).getWarWearinessModifier() + 100));
    iWarWearinessPercentAnger /= 100;

    if (!isHuman() && !isBarbarian() && !isMinorCiv())
    {
        iWarWearinessPercentAnger *= GC.getHandicapInfo(GC.getGameINLINE().getHandicapType()).getAIWarWearinessPercent();
        iWarWearinessPercentAnger /= 100;

        iWarWearinessPercentAnger *= std::max(0, ((GC.getHandicapInfo(GC.getGameINLINE().getHandicapType()).getAIPerEraModifier() * getCurrentEra()) + 100));
        iWarWearinessPercentAnger /= 100;
    }

    return iWarWearinessPercentAnger;
}

All values discussed here can be found in the GlobalDefines.xml.

We start by multiplying our war weariness with the BASE_WAR_WEARINESS_MULTIPLIER (2).

After that we check if either "Always war" or "Permanent war and peace" is active. If true we multiply our war weariness with (FORCED_WAR_WAR_WEARINESS_MODIFIER (-50 for BtS and -100 for CtH and RtR) + 100). By the way this is the way our mods ensure that no war weariness happens with these game options. Afterward we divide by 100.

Next in our code segment we check if this is a multiplayer game. If true we multiply our war weariness with (MULTIPLAYER_WAR_WEARINESS_MODIFIER (0) + 100) and divide by 100 afterward.

The next modifier is from the world size. We multiply our war weariness with (iWarWearinessModifier (-10 for Standard) + 100). Afterward we divide by 100.

The last modifers are only meant for AI players. These are the iAIWarWearinessPercent and iAIPerEraModifier modifiers.

And with that we have our final war weariness for this player. Finally let's do some examples:


Example a)

War Weariness towards A = (280 * 100) / 10000 = 2.8 = 2 thanks to integer calculation

Total War Weariness = 2

This goes into getModifiedWarWearinessPercentAnger:

2 * 2 = 4
No Game options set
Multiplayer is not a factor (0+100) / 100 is always the same
Standard size = (4 * (-10 + 100)) / 100 = 3.6 = 3 thanks to integer calculation

Final War Weariness = 3


Example b)

War Weariness towards A = (280 * 100) / 10000 = 2.8 = 2 thanks to integer calculation
War Weariness towards B = (400 * (100+100)) / 10000 = 8
War Weariness towards C = (2800 * 100) / 10000 = 28
B has SoZ

Total War Weariness = 38

This goes into getModifiedWarWearinessPercentAnger:

38 * 2 = 76
No Game options set
Multiplayer is not a factor (0+100) / 100 is always the same
Standard size = (76 * (-10 + 100)) / 100 = 68.4 = 68 thanks to integer calculation

Final War Weariness = 68



Lastly you may wonder what value is shown in the F4 advisor. Those are actually the same calculations as above, only for a single player and without the war check.

Summary

We can summarize all of this into the following formula

War Weariness towards one player = (Our teams war weariness towards that team * (100 + if enemy has Statue of Zeus = 100 otherwise 0)) / 10000
Total War Weariness = Sum of all War Weariness towards one player, with whom we are at war
Total Modified War Weariness = (Total War Weariness * 2) * World Size (iWarWearinessModifier + 100) / 100
Mods: RtR    CtH

Pitboss: PB39, PB40PB52, PB59 Useful Collections: Pickmethods, Mapmaking, Curious Civplayer

Buy me a coffee
Reply

How is the players war weariness converted into unhappiness for each city?

For the conversion part from player to city we look into CvCity and getWarWearinessPercentAnger

Code:
int CvCity::getWarWearinessPercentAnger() const
{
    int iAnger;

    iAnger = GET_PLAYER(getOwnerINLINE()).getWarWearinessPercentAnger();

    iAnger *= std::max(0, (getWarWearinessModifier() + GET_PLAYER(getOwnerINLINE()).getWarWearinessModifier() + 100));
    iAnger /= 100;

    return iAnger;
}

As you can see the first part is getting the war weariness from the player. This is the war weariness we calculated in the last question. In the next step we multiply that war weariness with the sum of all the cities multipliers. These are:

- Modifiers from buildings (Jails and in case of CtH Colosseums)
- Modifiers from wonders (Rushmore)
- Modifiers from Civics

We add all those up and add an additional 100 to that. As I've said this sum is multiplied with the war weariness.

Next step is dividing this by 100. Then we have the final anger percent for the city. To convert this into unhappiness, we look towards CvCity and unhappyLevel.

Code:
...
iUnhappiness = ((iAngerPercent * (getPopulation() + iExtra)) / GC.getPERCENT_ANGER_DIVISOR());
...

iAngerPercent is actually the sum of a lot of percentage based unhappiness values among those our getWarWearinessPercentAnger. iExtra is set to 0 if not specified and can be ignored for the most part in BtS. Lastly getPERCENT_ANGER_DIVISOR is found in the GlobalDefines.xml and set to 1000. And with that we have the unhappiness from war weariness. Let's do some examples again.


Example a)

War weariness = 3
No buildings
No Rushmore
No Civics

AngerPercent = 3 * (0 + 0 + 100) / 100 = 3

Population = 11

Unhappiness = ((3 * (11 + 0)) / 1000) = 0.033 = 0 thanks to integer calculation


Example b)

War weariness = 680
No buildings
No Rushmore
No Civics

AngerPercent = 680 * (0 + 0 + 100) / 100 = 680

Population = 11

Unhappiness = ((680 * (11 + 0)) / 1000) = 7.480 = 7 thanks to integer calculation


Example c)

War weariness = 680
Jail
No Rushmore
No Civics

AngerPercent = 680 * (-25 + 0 + 100) / 100 = 510

Population = 11

Unhappiness = ((510 * (11 + 0)) / 1000) = 5.610 = 5 thanks to integer calculation


Summary

We can summarize all of this into the following formula

War Weariness Anger Percent for the city = Players war weariness * (Buildingmodifier + Wondermodifiers + Civicmodifiers + 100) / 100
Unhappiness from War Weariness = ((War Weariness Anger Percent for the city * Population) / 1000);

Note: If (Buildingmodifier + Wondermodifiers + Civicmodifiers + 100) becomes negative. It instead is set to 0.


The one important thing that I learned here is that the civic and building modifiers do not have anything to do with the accumulation of war weariness. If you have high war weariness it's almost always useful to get them even if you stop fighting.
Mods: RtR    CtH

Pitboss: PB39, PB40PB52, PB59 Useful Collections: Pickmethods, Mapmaking, Curious Civplayer

Buy me a coffee
Reply

Did you handle the case that the above formula can yield negative values in CTH? tongue
Reply

This is handled by BtS code already. I will add that information.
Mods: RtR    CtH

Pitboss: PB39, PB40PB52, PB59 Useful Collections: Pickmethods, Mapmaking, Curious Civplayer

Buy me a coffee
Reply

(December 27th, 2020, 15:26)Charriu Wrote: It's important to not that for most values the culture of the combat tile is compared. The more culture you have on that tile the less war weariness you get. It's important to note that the culture from players with whom:

1. You are in a team or
2. Is your vassal or
3. You have open borders with

is counted towards the culture for both involved parties. The important formula for the comparison is:

Culture ratio = (100 * iTheirCulture) / (iOurCulture + iTheirCulture);

With Culture ratio = 100 with no culture present.

Lastly some more important features of the system in place:

- You do not get war weariness from barbarians
- If no unit is killed (Withdraw) no war weariness is added.
- Remember that open borders count towards the culture ratio.
- The team's war weariness value is not the value shown in the F4 advisor.

I hate reading the formulas so generally I only read the TLDR sections of these posts. With that said, can you clarify how exactly OB counts? Are you saying that if I am at war with Civ X, and I kill a unit from Civ X in Civ Y's territory and I have OB with Civ Y and Civ Y has 100% culture over the tile, I will not gain any WW?
Reply

It's a bit more complicated, because the culture from civ Y can be counted towards attacker and defender, who ever has OBs. From there we have three scenarios.

1. Only you have OB with Y: In this case you don't get WW
2. Only X has OB with Y: In this case you get the full WW
3. Both have OB: In this case the culture cancels each other out. It's the same as neutral ground and both get full WW.
Mods: RtR    CtH

Pitboss: PB39, PB40PB52, PB59 Useful Collections: Pickmethods, Mapmaking, Curious Civplayer

Buy me a coffee
Reply

So fighting in disputed border regions will give less WW than fighting in someone's core? I never knew that.
Reply

That's true. The more of your culture is present the better for you.
Mods: RtR    CtH

Pitboss: PB39, PB40PB52, PB59 Useful Collections: Pickmethods, Mapmaking, Curious Civplayer

Buy me a coffee
Reply

How does religion spread on its own?

For this we turn to CvCity and the doReligion method

Code:
if (getReligionCount() == 0)
{
    for (iI = 0; iI < GC.getNumReligionInfos(); iI++)
    {
        if (!isHasReligion((ReligionTypes)iI))
        {
            if ((iI == GET_PLAYER(getOwnerINLINE()).getStateReligion()) || !(GET_PLAYER(getOwnerINLINE()).isNoNonStateReligionSpread()))
            {
                [The interesting stuff.]
            }
        }
    }
}

So first we quickly get the boring stuff away:

- We check if there is no religion in the city
- We iterate through all religions and check if this religion is already present in the city (Don't ask me why this additional check is done)
- Lastly we check for theocracy - aka if the religion we are iterating across is the state religion or if the spreading of non-state religions is allowed.

With the boring stuff out of the way here; here comes the good stuff:

Code:
iRandThreshold = 0;

for (iJ = 0; iJ < MAX_PLAYERS; iJ++)
{
    if (GET_PLAYER((PlayerTypes)iJ).isAlive())
    {
        for (pLoopCity = GET_PLAYER((PlayerTypes)iJ).firstCity(&iLoop); pLoopCity != NULL; pLoopCity = GET_PLAYER((PlayerTypes)iJ).nextCity(&iLoop))
        {
            if (pLoopCity->isConnectedTo(this))
            {
                iSpread = pLoopCity->getReligionInfluence((ReligionTypes)iI);

                iSpread *= GC.getReligionInfo((ReligionTypes)iI).getSpreadFactor();

                if (iSpread > 0)
                {
                    iSpread /= std::max(1, (((GC.getDefineINT("RELIGION_SPREAD_DISTANCE_DIVISOR") * plotDistance(getX_INLINE(), getY_INLINE(), pLoopCity->getX_INLINE(), pLoopCity->getY_INLINE())) / GC.getMapINLINE().maxPlotDistance()) - 5));

                    iRandThreshold = std::max(iRandThreshold, iSpread);
                }
            }
        }
    }
}

if (GC.getGameINLINE().getSorenRandNum(GC.getDefineINT("RELIGION_SPREAD_RAND"), "Religion Spread") < iRandThreshold)
{
    setHasReligion(((ReligionTypes)iI), true, true, true);
    break;
}

We first establish a new variable iRandThreshold, which we will later use for the spread check.

We start by iterating across all alive players starting with the player with the lowest ID. After that we iterate across all cities of that player. If a city is connected to our city aka could have trade routes, we turn to the innermost calculations. Those are:

1. We get the religion influence of the connected city. This influence is the sum of all buildings in the city with the tag 'ReligionChanges' set in the BuildingInfo.xml and +1 for being the holy city. The influence is counted per individual religion and the only buildings that have this value set are the shrines, all of them with the value "1". TL:DR the religion influence is 1 for a holy city and 2 with a shrine of that religion.
2. Next we multiply the cities religion influence with the spreadfactor of the religion, which is set to 100 for every religion (ReligionInfo.xml). We only continue with the calculations if this value is greater then 0.
3. This next part gets a little bit more complicated. First of all we have the constant value RELIGION_SPREAD_DISTANCE_DIVISOR from GlobalDefines.XML, which is set to 100. The next part is the distance between the two cities. The last mystery part of the calculation is the infamous maxPlotDistance(), which by the way is also used in the distance maintenance calculation. It depends on the wrap mode and like the word already say it is the biggest distance between two points that you can create on the map at hand. With all that information we can make this calculation look a bit easier:

Code:
iSpread /= 100 / std::max(1, ((100 * distanceBetweenCities / maxPlotDistance) - 5));

I think it would be good to provide an example here. Let's say we have the following:

Cylindrical map with 30x20 dimension
City A on 5/10
City B on 8/10
Only a holy city is present

With these we get the following in the formula:

iSpread /= 100 / std::max(1, ((100 * 3 / 26) - 5)) = 100 / std::max(1, (11 - 5)) = 100 / 6 = iSpread / 16 = 100 / 16 = 6.25 = 6 due to integer calculation

4. Lastly in our inner calculation we check for the highest spread value and we only continue with that value.

We now have the highest spread chance to our city and the last step is to roll a number from 0 to RELIGION_SPREAD_RAND (found in GlobalDefines.xml) which is 100. If our number is smaller then our calculated spread chance, the city gets that religion and we stop the whole process.

Summary

Everybody already knows about the basic stuff for spreading (Random spreads only to cities without religion and no non-state spreading during Theocracy). Aside from those religions can only spread from holy cities that could have or have a trade connection to the city in question. The actual spread chance is calculated this way:

Spreadfactor = 100 for holy city and 200 for holy city + shrine
iSpread = Spreadfactor / (100 / std::max(1, ((100 * distanceBetweenCities / maxPlotDistance of the map) - 5)))

Now lastly because of the way this is calculated there is a hidden modifier. We check for each religion individually and in order. So if the first religion gets the spread the others are never considered. The order in which the religions are check is:

  1. Judaism
  2. Christianity
  3. Islam
  4. Hinduism
  5. Buddhism
  6. Confucianism
  7. Taoism
EDIT: Checked back at the calculation and fixed it in the analysis above.
Mods: RtR    CtH

Pitboss: PB39, PB40PB52, PB59 Useful Collections: Pickmethods, Mapmaking, Curious Civplayer

Buy me a coffee
Reply



Forum Jump: