My personal thread: Clarification of certain game mechanics

Holy city location

[[(10 + CityPopulation + SorenRandNum(1,10)) / (ReligionCount + 1)] / (IsthatCapital?: 8 : 1)]

Legends: [] means round down (or truncate) the value of the expression between such brackets.
SorenRandNum(1,10) is simply the famous RNG with a tag name. It gives 1 to 10 of a value.
ReligionCount + 1 : How many religions inside the city and +1 for avoiding the terrible "divide by zero black hole or BSOD".
(IsthatCapital? 8 : 1 ==> Ask the question yourself. If the city is a capital, return the value 8. Otherwise, for all other cities, it is 1, which means no real division.

City population certainly helps to favor some city you want to be the cradle of a religion, but what is the strongest parameter is indeed the religion count.
And bigger the religion count, less imposing is the effect indeed.

If we want to study the subject on deterministic grounds, we cut the RNG part and see religion count is the strongest factor to direct a religion fouding.
Another pop in a city, small effect (also caused by the constant 10). Another religion added or simply the presence of a single religion, big effect. If population was a pure multiplier, then it would have a big effect...
That works the same way as an inverse function.

Spoiler :


New cities are more favored because of the lack of a religion, then goes for highly populated cities, then loser capital takes it.

Fast fall for the very few values and slow down as numbers (religion count) go up.

Thread killed.
 
Finally, I was able to mostly gather about capitulee resource demands.
That always was a random part for me as I never knew how the AI would react. Sometimes, I got automatic war state in ridiculous situations (like the AI being far weaker than me).

Thus, this XML value (and following explanations) have a real strategic value in CIV4 gaming.

========================================================

SYNOPSIS

Two types of vassals exist: peacevassals and capitulee. While peacevassals have an automatic mechanics that force the AI to be FRIENDLY with you (even without enough attitude modifier), capitulee won't be necessary pleased with you as master. Both types enable disabled WFYABTA, many disabled reddened diplo items and right to demands single resources for free along other kind of bonuses/maluses.

Depending of the leader that has capitulated, if you have the right attitude, you can ask as many resources without risk at all of breaking the vassal treaty.
Indeed, under vassality, asking resources for free is some kind of animal intimidation fight: if one thinks have no chance, it flees. Otherwise it attacks.
Here the vassal revolts and enter war state when (s)he deemed the demand(s) was abusive.

It turns out that resource demands are either free-revolt if correct attitude or is under the same rules as a normal demand you do to a non-capitulee AI, but with war as result of refusal.

First demand is not protected from revolt and the vassal treaty can be broken even in the 10 first turns after capitulation. If there was some rumors of the first resource demand was free of dangers, it is often because on how long you knew/met the AI until deciding to capitulate him/her. This boost the chance of a granted first free resource. But once the first grant done, the magnitude of the second grant is reset to zero and you have to wait a certain number of turns before it worth a resource.

ANALYSIS

It all starts with CvPlayerAI.cpp (the SDK files that deal with individual AI leader behaviours)::AI_doDiplo().

For a willing to talk vassal, here's the code:

Spoiler :
Code:
if (GET_TEAM(GET_PLAYER((PlayerTypes)iI).getTeam()).isVassal(getTeam()))
								{
									iBestValue = 0;
									eBestGiveBonus = NO_BONUS;

									for (iJ = 0; iJ < GC.getNumBonusInfos(); iJ++)
									{
										if (GET_PLAYER((PlayerTypes)iI).getNumTradeableBonuses((BonusTypes)iJ) > 0 && getNumAvailableBonuses((BonusTypes)iJ) == 0)
										{
											iValue = AI_bonusTradeVal((BonusTypes)iJ, (PlayerTypes)iI, 1);

											if (iValue > iBestValue)
											{
												iBestValue = iValue;
												eBestGiveBonus = ((BonusTypes)iJ);
											}
										}
									}

									if (eBestGiveBonus != NO_BONUS)
									{
										theirList.clear();
										ourList.clear();

										setTradeItem(&item, TRADE_RESOURCES, eBestGiveBonus);
										theirList.insertAtEnd(item);

										if (GET_PLAYER((PlayerTypes)iI).isHuman())
										{
											if (!(abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()]))
											{
												CvPopupInfo* pInfo = new CvPopupInfo(BUTTONPOPUP_VASSAL_GRANT_TRIBUTE, getID(), eBestGiveBonus);
												if (pInfo)
												{
													gDLL->getInterfaceIFace()->addPopup(pInfo, (PlayerTypes)iI);
													abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()] = true;
												}
											}
										}
										else
										{
											GC.getGameINLINE().implementDeal(getID(), ((PlayerTypes)iI), &ourList, &theirList);
										}
									}
								}

It is basically how a code block on vassal-master trade of resources, with both side to offer or only one side (master) asking. Here is our part:

Spoiler :
Code:
if (GET_PLAYER((PlayerTypes)iI).isHuman())
										{
											if (!(abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()]))
											{
												CvPopupInfo* pInfo = new CvPopupInfo(BUTTONPOPUP_VASSAL_GRANT_TRIBUTE, getID(), eBestGiveBonus);
												if (pInfo)
												{
													gDLL->getInterfaceIFace()->addPopup(pInfo, (PlayerTypes)iI);
													abContacted[GET_PLAYER((PlayerTypes)iI).getTeam()] = true;
												}
											}
										}

First, demanding resources is excusive to human player! That means no one should ever see some war between AI vassal-AI master due to some resource denial.
BUTTONPOPUP_VASSAL_GRANT_TRIBUTE is some kind of action made by the human player and let's see it in details in CvDLLButtomPopup.ccp.

That's almost the core of the subject:

Spoiler :
Code:
case BUTTONPOPUP_VASSAL_DEMAND_TRIBUTE:
                if (pPopupReturn->getButtonClicked() < GC.getNumBonusInfos())
                {
                        PlayerTypes eVassal = (PlayerTypes)info.getData1();
                        if (GET_PLAYER(eVassal).isHuman())
                        {
                                CvPopupInfo* pInfo = new CvPopupInfo(BUTTONPOPUP_VASSAL_GRANT_TRIBUTE, GC.getGameINLINE().getActivePlayer(), pPopupReturn->getButtonClicked());
                                if (NULL != pInfo)
                                {
                                        gDLL->sendPopup(eVassal, pInfo);
                                }
                        }
                        else
                        {
                                TradeData item;
                                setTradeItem(&item, TRADE_RESOURCES, pPopupReturn->getButtonClicked());

                                CLinkList<TradeData> ourList;
                                CLinkList<TradeData> theirList;
                                theirList.insertAtEnd(item);

                                if (GET_PLAYER(eVassal).AI_considerOffer(GC.getGameINLINE().getActivePlayer(), &ourList, &theirList))
                                {
                                        gDLL->sendImplementDealMessage(eVassal, &ourList, &theirList);

                                        CvWString szBuffer = gDLL->getText("TXT_KEY_VASSAL_GRANT_TRIBUTE_ACCEPTED", GET_PLAYER(eVassal).getNameKey(), GET_PLAYER(GC.getGameINLINE().getActivePlayer()).getNameKey(), GC.getBonusInfo((BonusTypes)pPopupReturn->getButtonClicked()).getTextKeyWide());
                                        gDLL->getInterfaceIFace()->addMessage(GC.getGameINLINE().getActivePlayer(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer);
                                }
                                else
                                {
                                        gDLL->sendChangeWar(GET_PLAYER(eVassal).getTeam(), true);
                                }
                        }
                }
                break;

Let cut the fat of uninteresting:
Spoiler :
Code:
if (GET_PLAYER(eVassal).AI_considerOffer(GC.getGameINLINE().getActivePlayer(), &ourList, &theirList))
                                {
                                        gDLL->sendImplementDealMessage(eVassal, &ourList, &theirList);

                                        CvWString szBuffer = gDLL->getText("TXT_KEY_VASSAL_GRANT_TRIBUTE_ACCEPTED", GET_PLAYER(eVassal).getNameKey(), GET_PLAYER(GC.getGameINLINE().getActivePlayer()).getNameKey(), GC.getBonusInfo((BonusTypes)pPopupReturn->getButtonClicked()).getTextKeyWide());
                                        gDLL->getInterfaceIFace()->addMessage(GC.getGameINLINE().getActivePlayer(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer);
                                }
                                else
                                {
                                        gDLL->sendChangeWar(GET_PLAYER(eVassal).getTeam(), true);
                                }

Basically, it goes like this: if the AI (via AI_considerOffer() function) finds your demand reasonable, that function is returning true and the capitulee simply return a grumble plus the resource.
But if AI_considerOffer() returns false, then war is triggered. I haven't find the exact link between sending info a war and the actual war triggering, but I don't really care.
sendChangeWar() function is prolly obvious.

Here the code anyways:

Spoiler :
Code:
void CvMessageControl::sendChangeWar(TeamTypes eRivalTeam, bool bWar)
{
	if (NO_PLAYER != GC.getGameINLINE().getActivePlayer())
	{
		gDLL->sendMessageData(new CvNetChangeWar(GC.getGameINLINE().getActivePlayer(), eRivalTeam, bWar));
	}
}

Now where lies the acceptance of bullying from the capitulee lies in AI_considerOffer().
Yes, the same place as begging/demands functions.

Now the core of the subject (with fat cut):

Spoiler :
Code:
int iOurValue = GET_PLAYER(ePlayer).AI_dealVal(getID(), pOurList, false, iChange);
	int iTheirValue = AI_dealVal(ePlayer, pTheirList, false, iChange);

	if (iOurValue > 0 && 0 == pTheirList->getLength() && 0 == iTheirValue)
	{
		if (GET_TEAM(getTeam()).isVassal(GET_PLAYER(ePlayer).getTeam()) && CvDeal::isVassalTributeDeal(pOurList))
		{
			if (AI_getAttitude(ePlayer, false) <= GC.getLeaderHeadInfo(getPersonalityType()).getif()
				&& GET_TEAM(getTeam()).getAtWarCount(true) == 0
				&& GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getDefensivePactCount() == 0)
			{
				iOurValue *= (GET_TEAM(getTeam()).getPower(false) + 10);
				iOurValue /= (GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getPower(false) + 10);
			}
			else
			{
				return true;
			}
		}
		else
		{
			if (AI_getAttitude(ePlayer) < ATTITUDE_PLEASED)
			{
				if (GET_TEAM(getTeam()).getPower(false) > ((GET_TEAM(GET_PLAYER(ePlayer).getTeam()).getPower(false) * 4) / 3))
				{
					return false;
				}
			}

			if (AI_getMemoryCount(ePlayer, MEMORY_MADE_DEMAND_RECENT) > 0)
			{
				return false;
			}
		}
Basically for a player that is vassal and has the vassal treaty in their balance (i.e. a capitulee or peacevassal),
if the vassal attitude is lower or equal to <VassalRefuseAttitudeThreshold> (an XML value in CIV4LeaderHeadInfos.xml and the same used for peacevassal!!!!) AND not already at war (well duh!) AND the master does not have some defensive pact, then comes some calculi equivalent to normal demands.
BUT if you have at least one defensive pact, or have better relationship than <VassalRefuseAttitudeThreshold> , then resources are always granted!!!!
Peacevassals have an automatic FRIENDLY stance mechanics just to avoid refusal btw.

Now we now the ensured paths to get free-risk demands, let's see demands that are tied some many calculi and possibly I won't end this work being too heavy.

First, the AI ponders the demand via AI_dealVal() function:

Code:
iOurValue = GET_PLAYER(ePlayer).AI_dealVal(getID(), pOurList, false, iChange);

In CvPlayerAI::AI_dealVal(), hereunder the case for resources valuing:

Code:
case TRADE_RESOURCES:
			if (!bIgnoreAnnual)
			{
				iValue += AI_bonusTradeVal(((BonusTypes)(pNode->m_data.m_iData)), ePlayer, iChange);
			}
			break;
Thenb AI_bonusTradeVal is a simple function (where one can see vassals see their worth halved; for peacevassals only it seems, weird...), but it calls a big big and big function that gives the value of resources (AI_bonusVal). Despite I already unraveled this big scumbag function in the past for another reason, I won't display its ugliness for now unless begged.

At least, let show AI_bonusTradeVal():

Spoiler :
Code:
int CvPlayerAI::AI_bonusTradeVal(BonusTypes eBonus, PlayerTypes ePlayer, int iChange) const
{
	int iValue;

	FAssertMsg(ePlayer != getID(), "shouldn't call this function on ourselves");

	iValue = AI_bonusVal(eBonus, iChange);

	iValue *= ((std::min(getNumCities(), GET_PLAYER(ePlayer).getNumCities()) + 3) * 30);
	iValue /= 100;

	iValue *= std::max(0, (GC.getBonusInfo(eBonus).getAITradeModifier() + 100));
	iValue /= 100;

	if (GET_TEAM(GET_PLAYER(ePlayer).getTeam()).isVassal(getTeam()) && !GET_TEAM(GET_PLAYER(ePlayer).getTeam()).isCapitulated())
	{
		iValue /= 2;
	}

	return (iValue * GC.getDefineINT("PEACE_TREATY_LENGTH"));
}

To come back on iOurValue, then it is processed to a power inflating factor. Basically the power of the team (master+vassal(s)) over the power of the master power (I think).

I'll stop there. I need to rethink. I'm confusing things I think.

//cut
 
Possible next subject:

When the AI decides to create a colony? What is the trigger or the combination of many parameters?

I've seen many instances of a function that is logically tied to AI creating colonies.
Like this function:

AI_doSplit().
 
Nice thread, tachy :)

I've got 2 questions for when you have some time:

(1)Religion spread without missionary: what rules religion spread, coming from foreign and within the civ?

How strong is the impact of a first religion already present in a city holding it back from "catching" other religions? After a city gets its first religion, is it worth to wait (in a civ with many religions or contact w/ many religions) or missionary the only way?

(2)Mine resource pops: is it only a RNG chance or there is more to it? Apart from having the necessary tech for iron, copper, etc.

I'd appreciate if you check them :)
 
1. The city needs to be connected via trade routes I think is one of the requirements, but I know for sure it is extremely RNG based.

2. RNG based.
 
Nice thread, tachy :)

I've got 2 questions for when you have some time:

(1)Religion spread without missionary: what rules religion spread, coming from foreign and within the civ?

How strong is the impact of a first religion already present in a city holding it back from "catching" other religions? After a city gets its first religion, is it worth to wait (in a civ with many religions or contact w/ many religions) or missionary the only way?

(2)Mine resource pops: is it only a RNG chance or there is more to it? Apart from having the necessary tech for iron, copper, etc.

I'd appreciate if you check them :)

It's friday, I got time! [party]

1) Religion is indeed a RNG-based process.
First, in the post about holy cities, it was mentioned natural religion spreading are triggered by holy cities and RNG drops to 0 if the city has at least one religion present. Religion spreading from holy cities are only towards pagan cities.
You can wait their missionaries, but you can't count on freebee religions.

2)Mining resources popping sure is an ultra goody hut to me if the right resource is spawned, but you certainly can't rely on this at all. The popping by itself is one of tiniest of the game (a chance because otherwise slightly gamebreaking in official competitions).

Let do some code diving:

Spoiler :
Code:
void CvPlot::doImprovement()
{
	PROFILE_FUNC();

	CvCity* pCity;
	CvWString szBuffer;
	int iI;

	FAssert(isBeingWorked() && isOwned());

	if (getImprovementType() != NO_IMPROVEMENT)
	{
		if (getBonusType() == NO_BONUS)
		{
			FAssertMsg((0 < GC.getNumBonusInfos()), "GC.getNumBonusInfos() is not greater than zero but an array is being allocated in CvPlot::doImprovement");
			for (iI = 0; iI < GC.getNumBonusInfos(); ++iI)
			{
				if (GET_TEAM(getTeam()).isHasTech((TechTypes)(GC.getBonusInfo((BonusTypes) iI).getTechReveal())))
				{
					if (GC.getImprovementInfo(getImprovementType()).getImprovementBonusDiscoverRand(iI) > 0)
					{
						if (GC.getGameINLINE().getSorenRandNum(GC.getImprovementInfo(getImprovementType()).getImprovementBonusDiscoverRand(iI), "Bonus Discovery") == 0)
						{
							setBonusType((BonusTypes)iI);

							pCity = GC.getMapINLINE().findCity(getX_INLINE(), getY_INLINE(), getOwnerINLINE(), NO_TEAM, false);

							if (pCity != NULL)
							{
								szBuffer = gDLL->getText("TXT_KEY_MISC_DISCOVERED_NEW_RESOURCE", GC.getBonusInfo((BonusTypes) iI).getTextKeyWide(), pCity->getNameKey());
								gDLL->getInterfaceIFace()->addMessage(getOwnerINLINE(), false, GC.getEVENT_MESSAGE_TIME(), szBuffer, "AS2D_DISCOVERBONUS", MESSAGE_TYPE_MINOR_EVENT, GC.getBonusInfo((BonusTypes) iI).getButton(), (ColorTypes)GC.getInfoTypeForString("COLOR_WHITE"), getX_INLINE(), getY_INLINE(), true, true);
							}

							break;
						}
					}
				}
			}
		}
	}

	doImprovementUpgrade();
}

That's the part in CvPlot.cpp file (rather telling by its name).
Basically take it as a enumeration of each possible resource enabled (revealed) by a tech and each of those enabled resources have their RNG value on their own.
This information is located in Assets/XML (files easy to modify to change the game flavor)/Terrain/CIV4ImprovementInfos.xml.

There are 8 mining bonuses with affected RNG:

An example:

Code:
<BonusTypeStruct>
					<BonusType>BONUS_GEMS</BonusType>
					<bBonusMakesValid>1</bBonusMakesValid>
					<bBonusTrade>1</bBonusTrade>
					<iDiscoverRand>10000</iDiscoverRand>
					<YieldChanges>
						<iYieldChange>0</iYieldChange>
						<iYieldChange>-1</iYieldChange>
						<iYieldChange>5</iYieldChange>
					</YieldChanges>
				</BonusTypeStruct>

iDiscoverRand is the RNG value by itself and it works like this: imagine a virtual dice rolling a value from 0 to 10000 (that makes 10001 possible values, but I'll ignore it and make it to 10000). If the virtual dice rolls 0, then for the respective resource (gems in our example), it appears.

But, you see, 1/10000 is 0.01%, which is extremely small. This is good to avoid gamebreaking bonus for early game and later game popping resources (due to larger number of possible mining resoures plus mines) have less impact.

As I said, there are 8 resources, this is 8/10000 odd to get one mining bonus in the late game for each mine worked..

Interesting how devs see this event as a minor one btw.

Lastly, the actual popping is proceded in another part of the code where it checks if the mine is actually worked:

Spoiler :
Code:
void CvPlot::doImprovementUpgrade()
{
	if (getImprovementType() != NO_IMPROVEMENT)
	{
		ImprovementTypes eImprovementUpdrade = (ImprovementTypes)GC.getImprovementInfo(getImprovementType()).getImprovementUpgrade();
		if (eImprovementUpdrade != NO_IMPROVEMENT)
		{
			if (isBeingWorked() || GC.getImprovementInfo(eImprovementUpdrade).isOutsideBorders())
			{
				changeUpgradeProgress(GET_PLAYER(getOwnerINLINE()).getImprovementUpgradeRate());

				if (getUpgradeProgress() >= GC.getGameINLINE().getImprovementUpgradeTime(getImprovementType()))
				{
					setImprovementType(eImprovementUpdrade);
				}
			}
		}
	}
}

Perhaps I'm wrong in the second part because I find it weird (to my level) that all hills with a mine will procees a dice roll and then those being worked are chosen to let resources make surface if the dice roll is the good for that worked mine. It seems CPU consuming to me. But at my level I don't have the computer engineering knowledge in that subject.
 
A question for Tachy :D

How do you calculate a Civics upkeep? It says High, Medium, Low or None on the screen, but I'm sure there must be some formula to calculate the numerical cost. :)
 
@tachywaxon:

Just a post to say I really like your current Avatar. In my opinion, the Quetzal is the most beautiful bird in the world.

Sun Tzu Wu
 
A question for Tachy :D

How do you calculate a Civics upkeep? It says High, Medium, Low or None on the screen, but I'm sure there must be some formula to calculate the numerical cost. :)

A Spy has been discovered!
50% to discover ID===> Success?

ID: Unusual Suspect

Back off.

Spoiler :
:joke:
But this kind of question, which is ultra micro intensive makes me think things. :p

Yes, there are formulae (even on the forums too). Will come to this subject likely tomorrow. SGOTM is making my head scratching hard now.

 
@tachywaxon:

Just a post to say I really like your current Avatar. In my opinion, the Quetzal is the most beautiful bird in the world.

Sun Tzu Wu

Knowing your gnawing love for knowledge (just like I do about raw information), this bird is the turaco.
 
:lol: The arguments in US are something else entirely and completely unrelated ;) But feel free to answer this after the SGOTM ends. No particular rush, I'll still be playing Civ then :D
 
:lol: The arguments in US are something else entirely and completely unrelated ;) But feel free to answer this after the SGOTM ends. No particular rush, I'll still be playing Civ then :D

I meant I can't answer today, not in the next 103 days. :lol:
 
Hello Tachywaxon, nice avatar as others have said. :)

I have a few questions about how promotions affect a unit's health:
  • Is the amount of health a wounded unit heals when promoted completely random?
  • Is there a cap on how much health a unit can regain after a promotion?
  • Are some promos better than others for gaining health? (Say Combat vs. Medic or something else)
Thank you for your time. :)
 
Knowing your gnawing love for knowledge (just like I do about raw information), this bird is the turaco.

Thanks for the correction. The turaco does look like a quetzal in that they have similar heads and the same green color, but the quetzal doesn't have red bans around its eyes and no white near the eyes either. The turaco has both.

Sorry about the off topic comments.

Sun Tzu Wu
 
All fine. This off-topic comment has goodness.
On the other hand, in another thread, mention of "scat porn" and then pounce onto me agressively out of blue, that's wierd and off-topic.

@Smilingrogue

Will see this matter tomorrow. I have the answer but I have to go to bed.
 
What even weirder is the same guy commented my Youtube channel name on my visitor wall for no apparent reason.
 
How do you calculate a Civics upkeep? It says High, Medium, Low or None on the screen, but I'm sure there must be some formula to calculate the numerical cost. :)

This has definitely been answered (with reference to the SDK) before. My memory is that the most important point is that the difference between Low and None is much greater than the difference between Low and High, even.

I have a few questions about how promotions affect a unit's health:

A unit is healed one-half the damage it has taken when promoted. (Additionally, it might then receive the normal benefits from promotions such as March).
 
A unit is healed one-half the damage it has taken when promoted. (Additionally, it might then receive the normal benefits from promotions such as March).

So, if an Axeman is at 3 health, he will get 1 health back from a promotion? (Half of 2 lost hit points) and if I give him march, he will get 1 hit point back and also start healing right away on that turn even if he moves? Did I understand this correctly?
 
So, if an Axeman is at 3 health, he will get 1 health back from a promotion? (Half of 2 lost hit points)

Any unit that was at 60% health will be at 80% health after a promotion.

and if I give him march, he will get 1 hit point back and also start healing right away on that turn even if he moves? Did I understand this correctly?

March would then work normally at the end of the turn, yes.
 
Top Bottom