Question: Promotions - AI Choices

Officer Reene

It hates you all
Joined
Jan 21, 2006
Messages
698
Location
In the middle of nowhere
I have made new promotions...

However the AI sometimes uses them... but often it does not...

Is there a "XML" way to force an AI to pick a promotion, kinda like the <weight> in the building, tech, and unit XML files?

I noticed the promotions dont have a weight tagline :mad: and I can figure another way to do it:confused:

is it even possible to do it in XML?

or would I have to resort to python or SDK?


any help would be greatly appreciated :)
 
Officer Reene said:
I have made new promotions...

However the AI sometimes uses them... but often it does not...

Is there a "XML" way to force an AI to pick a promotion, kinda like the <weight> in the building, tech, and unit XML files?

I noticed the promotions dont have a weight tagline :mad: and I can figure another way to do it:confused:

is it even possible to do it in XML?

or would I have to resort to python or SDK?


any help would be greatly appreciated :)

I don't think you can in XML really affect the way an AI promotes it's units. The only way really to kinda rib the AI into using your promotion is making it really useful so that the AI will pick it. This will probably just make it overpowering though.

For one thing, I think you can change the UnitAI python function in CvGameUtils to probably do this, making sure you check that the player is an AI player and then picking the promotion for them.

If it helps, you can take a look at the AI code. It seems pretty straightforward. It just goes through all the different changes you can do and, depending on the UnitAIType of the unit, adds something to the iValue variable.

Each one of the promotions that a unit can do goes through this function, and the one picked is the one that recieved the highest score.

Note the randomness at the end which keeps the AI varied.

Spoiler :

Code:
int CvUnitAI::AI_promotionValue(PromotionTypes ePromotion)
{
	int iValue;
	int iTemp;
	int iI;

	iValue = 0;

	if (GC.getPromotionInfo(ePromotion).isBlitz())
	{
		if (AI_getUnitAIType() == UNITAI_RESERVE)
		{
			iValue += 10;
		}
		else
		{
			iValue += 2;
		}
	}

	if (GC.getPromotionInfo(ePromotion).isAmphib())
	{
		if ((AI_getUnitAIType() == UNITAI_ATTACK) ||
			  (AI_getUnitAIType() == UNITAI_ATTACK_CITY))
		{
			iValue += 5;
		}
		else
		{
			iValue++;
		}
	}

	if (GC.getPromotionInfo(ePromotion).isRiver())
	{
		if ((AI_getUnitAIType() == UNITAI_ATTACK) ||
			  (AI_getUnitAIType() == UNITAI_ATTACK_CITY))
		{
			iValue += 5;
		}
		else
		{
			iValue++;
		}
	}

	if (GC.getPromotionInfo(ePromotion).isEnemyRoute())
	{
		if (AI_getUnitAIType() == UNITAI_PILLAGE)
		{
			iValue += 40;
		}
		else if ((AI_getUnitAIType() == UNITAI_ATTACK) ||
			       (AI_getUnitAIType() == UNITAI_ATTACK_CITY))
		{
			iValue += 20;
		}
		else
		{
			iValue += 4;
		}
	}

	if (GC.getPromotionInfo(ePromotion).isAlwaysHeal())
	{
		if ((AI_getUnitAIType() == UNITAI_ATTACK) ||
			  (AI_getUnitAIType() == UNITAI_ATTACK_CITY) ||
				(AI_getUnitAIType() == UNITAI_PILLAGE) ||
				(AI_getUnitAIType() == UNITAI_COUNTER) ||
				(AI_getUnitAIType() == UNITAI_ATTACK_SEA) ||
				(AI_getUnitAIType() == UNITAI_ESCORT_SEA))
		{
			iValue += 10;
		}
		else
		{
			iValue += 8;
		}
	}

	if (GC.getPromotionInfo(ePromotion).isHillsDoubleMove())
	{
		if (AI_getUnitAIType() == UNITAI_EXPLORE)
		{
			iValue += 20;
		}
		else
		{
			iValue += 1;
		}
	}

	if (GC.getPromotionInfo(ePromotion).isImmuneToFirstStrikes())
	{
		if ((AI_getUnitAIType() == UNITAI_ATTACK) ||
			  (AI_getUnitAIType() == UNITAI_ATTACK_CITY))
		{
			iValue += 8;
		}
		else
		{
			iValue += 4;
		}
	}

	iTemp = GC.getPromotionInfo(ePromotion).getVisibilityChange();
	if (AI_getUnitAIType() == UNITAI_EXPLORE_SEA)
	{
		iValue += (iTemp * 40);
	}
	else
	{
		iValue += (iTemp * 1);
	}

	iTemp = GC.getPromotionInfo(ePromotion).getMovesChange();
	if ((AI_getUnitAIType() == UNITAI_ATTACK_SEA) ||
		  (AI_getUnitAIType() == UNITAI_RESERVE_SEA) ||
		  (AI_getUnitAIType() == UNITAI_ESCORT_SEA) ||
			(AI_getUnitAIType() == UNITAI_EXPLORE_SEA) ||
			(AI_getUnitAIType() == UNITAI_ASSAULT_SEA) ||
			(AI_getUnitAIType() == UNITAI_SETTLER_SEA))
	{
		iValue += (iTemp * 20);
	}
	else
	{
		iValue += (iTemp * 4);
	}

	iTemp = GC.getPromotionInfo(ePromotion).getMoveDiscountChange();
	if (AI_getUnitAIType() == UNITAI_PILLAGE)
	{
		iValue += (iTemp * 10);
	}
	else
	{
		iValue += (iTemp * 2);
	}

	iTemp = GC.getPromotionInfo(ePromotion).getFirstStrikesChange();
	if ((AI_getUnitAIType() == UNITAI_RESERVE) ||
		  (AI_getUnitAIType() == UNITAI_COUNTER) ||
			(AI_getUnitAIType() == UNITAI_CITY_DEFENSE) ||
			(AI_getUnitAIType() == UNITAI_CITY_COUNTER) ||
			(AI_getUnitAIType() == UNITAI_CITY_SPECIAL) ||
			(AI_getUnitAIType() == UNITAI_RESERVE_SEA) ||
			(AI_getUnitAIType() == UNITAI_CARRIER_SEA))
	{
		iValue += (iTemp * 20);
	}
	else
	{
		iValue += (iTemp * 8);
	}

	iTemp = GC.getPromotionInfo(ePromotion).getChanceFirstStrikesChange();
	if ((AI_getUnitAIType() == UNITAI_RESERVE) ||
		  (AI_getUnitAIType() == UNITAI_COUNTER) ||
		  (AI_getUnitAIType() == UNITAI_CITY_DEFENSE) ||
			(AI_getUnitAIType() == UNITAI_CITY_COUNTER) ||
			(AI_getUnitAIType() == UNITAI_CITY_SPECIAL) ||
			(AI_getUnitAIType() == UNITAI_RESERVE_SEA) ||
			(AI_getUnitAIType() == UNITAI_CARRIER_SEA))
	{
		iValue += (iTemp * 10);
	}
	else
	{
		iValue += (iTemp * 4);
	}

	iTemp = GC.getPromotionInfo(ePromotion).getWithdrawalChange();
	if ((AI_getUnitAIType() == UNITAI_COLLATERAL) ||
		  (AI_getUnitAIType() == UNITAI_RESERVE) ||
		  (AI_getUnitAIType() == UNITAI_RESERVE_SEA))
	{
		iValue += (iTemp * 1);
	}
	else
	{
		iValue += (iTemp / 4);
	}

	iTemp = GC.getPromotionInfo(ePromotion).getCollateralDamageChange();
	if (AI_getUnitAIType() == UNITAI_COLLATERAL)
	{
		iValue += (iTemp * 2);
	}
	else if (AI_getUnitAIType() == UNITAI_ATTACK_CITY)
	{
		iValue += (iTemp * 1);
	}
	else
	{
		iValue += (iTemp / 8);
	}

	iTemp = GC.getPromotionInfo(ePromotion).getBombardRateChange();
	if (AI_getUnitAIType() == UNITAI_ATTACK_CITY)
	{
		iValue += (iTemp * 1);
	}
	else
	{
		iValue += (iTemp / 8);
	}

	iTemp = GC.getPromotionInfo(ePromotion).getEnemyHealChange();
	if ((AI_getUnitAIType() == UNITAI_ATTACK) ||
		  (AI_getUnitAIType() == UNITAI_ATTACK_SEA))
	{
		iValue += (iTemp / 4);
	}
	else
	{
		iValue += (iTemp / 8);
	}

	iTemp = GC.getPromotionInfo(ePromotion).getNeutralHealChange();
	iValue += (iTemp / 8);

	iTemp = GC.getPromotionInfo(ePromotion).getFriendlyHealChange();
	if ((AI_getUnitAIType() == UNITAI_CITY_DEFENSE) ||
		  (AI_getUnitAIType() == UNITAI_CITY_COUNTER) ||
		  (AI_getUnitAIType() == UNITAI_CITY_SPECIAL))
	{
		iValue += (iTemp / 4);
	}
	else
	{
		iValue += (iTemp / 8);
	}

	iTemp = GC.getPromotionInfo(ePromotion).getSameTileHealChange();
	if ((AI_getUnitAIType() == UNITAI_COUNTER) ||
		  (AI_getUnitAIType() == UNITAI_CITY_DEFENSE) ||
		  (AI_getUnitAIType() == UNITAI_CITY_COUNTER) ||
		  (AI_getUnitAIType() == UNITAI_CITY_SPECIAL))
	{
		iValue += (iTemp * 1);
	}
	else
	{
		iValue += (iTemp / 8);
	}

	iTemp = GC.getPromotionInfo(ePromotion).getAdjacentTileHealChange();
	if (AI_getUnitAIType() == UNITAI_ATTACK)
	{
		iValue += (iTemp * 2);
	}
	else
	{
		iValue += (iTemp / 4);
	}

	iTemp = GC.getPromotionInfo(ePromotion).getCombatPercent();
	if ((AI_getUnitAIType() == UNITAI_ATTACK) ||
		  (AI_getUnitAIType() == UNITAI_ATTACK_SEA) ||
		  (AI_getUnitAIType() == UNITAI_RESERVE_SEA) ||
			(AI_getUnitAIType() == UNITAI_ESCORT_SEA) ||
			(AI_getUnitAIType() == UNITAI_CARRIER_SEA))
	{
		iValue += (iTemp * 2);
	}
	else
	{
		iValue += (iTemp * 1);
	}

	iTemp = GC.getPromotionInfo(ePromotion).getCityAttackPercent();
	if (AI_getUnitAIType() == UNITAI_ATTACK_CITY)
	{
		iValue += (iTemp * 1);
	}
	else if ((AI_getUnitAIType() == UNITAI_ATTACK) ||
		       (AI_getUnitAIType() == UNITAI_COUNTER))
	{
		iValue += (iTemp / 2);
	}
	else
	{
		iValue += (iTemp / 4);
	}

	iTemp = GC.getPromotionInfo(ePromotion).getCityDefensePercent();
	if ((AI_getUnitAIType() == UNITAI_CITY_DEFENSE) ||
		  (AI_getUnitAIType() == UNITAI_CITY_COUNTER) ||
		  (AI_getUnitAIType() == UNITAI_CITY_SPECIAL))
	{
		iValue += (iTemp * 2);
	}
	else
	{
		iValue += (iTemp / 4);
	}

	iTemp = GC.getPromotionInfo(ePromotion).getHillsDefensePercent();
	if (AI_getUnitAIType() == UNITAI_COUNTER)
	{
		iValue += (iTemp / 4);
	}
	else
	{
		iValue += (iTemp / 16);
	}

	for (iI = 0; iI < GC.getNumTerrainInfos(); iI++)
	{
		iTemp = GC.getPromotionInfo(ePromotion).getTerrainDefensePercent(iI);
		if (AI_getUnitAIType() == UNITAI_COUNTER)
		{
			iValue += (iTemp / 4);
		}
		else
		{
			iValue += (iTemp / 16);
		}

		if (GC.getPromotionInfo(ePromotion).getTerrainDoubleMove(iI))
		{
			if (AI_getUnitAIType() == UNITAI_EXPLORE)
			{
				iValue += 20;
			}
			else
			{
				iValue += 1;
			}
		}
	}

	for (iI = 0; iI < GC.getNumFeatureInfos(); iI++)
	{
		iTemp = GC.getPromotionInfo(ePromotion).getFeatureDefensePercent(iI);
		if (AI_getUnitAIType() == UNITAI_COUNTER)
		{
			iValue += (iTemp / 4);
		}
		else
		{
			iValue += (iTemp / 16);
		}

		if (GC.getPromotionInfo(ePromotion).getFeatureDoubleMove(iI))
		{
			if (AI_getUnitAIType() == UNITAI_EXPLORE)
			{
				iValue += 20;
			}
			else
			{
				iValue += 1;
			}
		}
	}

	for (iI = 0; iI < GC.getNumUnitCombatInfos(); iI++)
	{
		iTemp = GC.getPromotionInfo(ePromotion).getUnitCombatModifierPercent(iI);
		if (AI_getUnitAIType() == UNITAI_COUNTER)
		{
			iValue += (iTemp * 1);
		}
		else if ((AI_getUnitAIType() == UNITAI_ATTACK) ||
			       (AI_getUnitAIType() == UNITAI_RESERVE))
		{
			iValue += (iTemp / 2);
		}
		else
		{
			iValue += (iTemp / 8);
		}
	}

	for (iI = 0; iI < NUM_DOMAIN_TYPES; iI++)
	{
		iTemp = ((int)(GC.getPromotionInfo(ePromotion).getDomainModifierPercent(iI) * 100.0f));
		if (AI_getUnitAIType() == UNITAI_COUNTER)
		{
			iValue += (iTemp * 1);
		}
		else if ((AI_getUnitAIType() == UNITAI_ATTACK) ||
			       (AI_getUnitAIType() == UNITAI_RESERVE))
		{
			iValue += (iTemp / 2);
		}
		else
		{
			iValue += (iTemp / 8);
		}
	}

	if (iValue > 0)
	{
		iValue += GC.getGameINLINE().getSorenRandNum(15, "AI Promote");
	}

	return iValue;
}
 
Gerikes said:
...For one thing, I think you can change the UnitAI python function in CvGameUtils to probably do this, making sure you check that the player is an AI player and then picking the promotion for them....

I have no idea how to code... but thx Gerikes... that code does looks straightforward.....

If Im brave enough...I'll try it... :eek:

but since I have no clue about python ...where does this code go?


Thanks again for your advice :)
 
Lord Olleus said:
Personaly, I think that picking promotions is one of the things that the AI is best at, why change it?


I had some weird experiences with the AI's choice of promotions... :blush:

*shrugs* I guess the experience is different for all :rolleyes:
 
Lord Olleus said:
Personaly, I think that picking promotions is one of the things that the AI is best at, why change it?

That's not correct. The AI doesn't understand the it's smart to put Shock on axemen but not on spearmen.
 
Officer Reene said:
I have no idea how to code... but thx Gerikes... that code does looks straightforward.....

If Im brave enough...I'll try it... :eek:

but since I have no clue about python ...where does this code go?


Thanks again for your advice :)

I Must've messed up my words a bit....

You could probably do it in AI_unitUpdate, in the CvGameUtils. Check if the pUnit has the ability to promote and what promotions it can do, and decide for it. You don't have to make the decision, you can check to see if any of yours is a good idea, and if not, do nothing and the AI will decide itself.

I've never done this, so I can't say for sure it will work, but I don't see any reason why not.

If you're not a python person, well, it might be a bit harder. But there's nothing for the promotions like the ai weight that there is for buildings.
 
Back
Top Bottom