Need AI Assistance

LyTning94

Dragonborn
Joined
Nov 10, 2010
Messages
397
Location
Skyrim
I have added a diplomat unit with a variety of new features. To be supported by the AI, I attempted to add a UNITAI_DIPLOMAT to the SDK. I added it in CvEnums.h, exposed it to python in CyEnumsInterface.cpp, added it to CvGameCoreUtils.cpp, and added this code to CvUnitAI::AI_update() (creating CvUnitAI::AI_diplomatMove() obviously):

Code:
case UNITAI_DIPLOMAT:
	AI_diplomatMove();
	break;

When I load my mod, this error appears:

Assert Failed

File: CvGlobals.cpp
Line: 3493
Expression: strcmp(szType, "NONE")==0 || strcmp(szType, "")==0
Message: info type UNITAI_DIPLOMAT not found, Current XML file is: xml\Units/CIV4UnitInfos.xml

So I have a few questions:

1) Where else do I have to add UNITAI_DIPLOMAT to get it to load? (I did a full project search for UNITAI_MISSIONARY to give myself an idea of where to add code, so I know there are other places where I need to add this--which leads me to my other questions--but I thought I covered everything that would allow the mod to load...:dunno:)

2) I found the other UnitAIs appear in CvUnitAI::AI_groupFirstVal(), where each UnitAI returns an integer. Any idea what this function does?

3) Some other functions I found the UnitAIs in were CvPlayerAI::AI_bestTech(), CvPlayerAI::AI_unitValue(), CvTeamAI::AI_getTechMonopolyValue(), CvPlayer::disbandUnit(), and CvPlayerAI::AI_disbandUnit(). How important is it to add a new UNITAI to these functions? If it is necessary, I may need some guidance on what to add.

Thanks in advance!
 
The unitai's are enumerated in Assets/XML/BasicInfos/CIV4UnitAIInfos.xml. Add yours there, being sure to keep it in the same order you have in CvEnums.h.
 
Sorry for the late response, I haven't had time to work on my mod in the last week or so.

I added the UNITAI in Civ4UnitAIInfos as you said, but now I get this error while loading:

Assert Failed

File: CvXMLLoadUtilitySet.cpp
Line: 1295
Expression: bSuccess
Message:

When this error appears, Civ4UnitAIInfos.xml is the last item loaded in the output frame of VC (while debugging).

If I ignore the error and start a game, the first turn the AI has a unit with my new UNITAI, I get this error:

Assert Failed

File: CvPlayerAI.cpp
Line: 7955
Expression: false
Message:

If I ignore this error, the game appears to be going in an infinite loop or something of that nature.

Here is the code which causes the second assert to fail (the entire function):
Code:
int CvPlayerAI::AI_unitValue(UnitTypes eUnit, UnitAITypes eUnitAI, CvArea* pArea) const
{
	bool bValid;
	int iNeededMissionaries;
	int iCombatValue;
	int iValue;
	int iTempValue;
	int iI;

	FAssertMsg(eUnit != NO_UNIT, "Unit is not assigned a valid value");
	FAssertMsg(eUnitAI != NO_UNITAI, "UnitAI is not assigned a valid value");

	if (GC.getUnitInfo(eUnit).getDomainType() != AI_unitAIDomainType(eUnitAI))
	{
		if (eUnitAI != UNITAI_ICBM)//XXX
		{
			return 0;
		}
	}

	if (GC.getUnitInfo(eUnit).getNotUnitAIType(eUnitAI))
	{
		return 0;
	}

	bValid = GC.getUnitInfo(eUnit).getUnitAIType(eUnitAI);

	if (!bValid)
	{
		switch (eUnitAI)
		{
		case UNITAI_UNKNOWN:
			break;

		case UNITAI_ANIMAL:
			if (GC.getUnitInfo(eUnit).isAnimal())
			{
				bValid = true;
			}
			break;

		case UNITAI_SETTLE:
			if (GC.getUnitInfo(eUnit).isFound())
			{
				bValid = true;
			}
			break;

		case UNITAI_WORKER:
			for (iI = 0; iI < GC.getNumBuildInfos(); iI++)
			{
				if (GC.getUnitInfo(eUnit).getBuilds(iI))
				{
					bValid = true;
					break;
				}
			}
			break;

		case UNITAI_ATTACK:
			if (GC.getUnitInfo(eUnit).getCombat() > 0)
			{
				if (!(GC.getUnitInfo(eUnit).isOnlyDefensive()))
				{
					bValid = true;
				}
			}
			break;

		case UNITAI_ATTACK_CITY:
			if (GC.getUnitInfo(eUnit).getCombat() > 0)
			{
				if (!(GC.getUnitInfo(eUnit).isOnlyDefensive()))
				{
					if (!(GC.getUnitInfo(eUnit).isNoCapture()))
					{
						bValid = true;
					}
				}
			}
			break;

		case UNITAI_COLLATERAL:
			if (GC.getUnitInfo(eUnit).getCombat() > 0)
			{
				if (!(GC.getUnitInfo(eUnit).isOnlyDefensive()))
				{
					if (GC.getUnitInfo(eUnit).getCollateralDamage() > 0)
					{
						bValid = true;
					}
				}
			}
			break;

		case UNITAI_PILLAGE:
			if (GC.getUnitInfo(eUnit).getCombat() > 0)
			{
				if (!(GC.getUnitInfo(eUnit).isOnlyDefensive()))
				{
					bValid = true;
				}
			}
			break;

		case UNITAI_RESERVE:
			if (GC.getUnitInfo(eUnit).getCombat() > 0)
			{
				if (!(GC.getUnitInfo(eUnit).isOnlyDefensive()))
				{
						bValid = true;
					}
				}
			break;

		case UNITAI_COUNTER:
			if (GC.getUnitInfo(eUnit).getCombat() > 0)
			{
				if (!(GC.getUnitInfo(eUnit).isOnlyDefensive()))
				{
					if (GC.getUnitInfo(eUnit).getInterceptionProbability() > 0)
					{
						bValid = true;
					}

					if (!bValid)
					{
						for (iI = 0; iI < GC.getNumUnitClassInfos(); iI++)
						{
							if (GC.getUnitInfo(eUnit).getUnitClassAttackModifier(iI) > 0)
							{
								bValid = true;
								break;
							}

							if (GC.getUnitInfo(eUnit).getTargetUnitClass(iI))
							{
								bValid = true;
								break;
							}
						}
					}

					if (!bValid)
					{
						for (iI = 0; iI < GC.getNumUnitCombatInfos(); iI++)
						{
							if (GC.getUnitInfo(eUnit).getUnitCombatModifier(iI) > 0)
							{
								bValid = true;
								break;
							}

							if (GC.getUnitInfo(eUnit).getTargetUnitCombat(iI))
							{
								bValid = true;
								break;
							}
						}
					}

					if (!bValid)
					{

						for (iI = 0; iI < GC.getNumUnitInfos(); iI++)
						{
							int iUnitClass = GC.getUnitInfo(eUnit).getUnitClassType();
							if (NO_UNITCLASS != iUnitClass && GC.getUnitInfo((UnitTypes)iI).getDefenderUnitClass(iUnitClass))
							{
								bValid = true;
								break;
							}

							int iUnitCombat = GC.getUnitInfo(eUnit).getUnitCombatType();
							if (NO_UNITCOMBAT !=  iUnitCombat && GC.getUnitInfo((UnitTypes)iI).getDefenderUnitCombat(iUnitCombat))
							{
								bValid = true;
								break;
							}
						}
					}
				}
			}
			break;

		case UNITAI_CITY_DEFENSE:
			if (GC.getUnitInfo(eUnit).getCombat() > 0)
			{
				if (!(GC.getUnitInfo(eUnit).isNoDefensiveBonus()))
				{
					if (GC.getUnitInfo(eUnit).getCityDefenseModifier() > 0)
					{
						bValid = true;
					}
				}
			}
			break;

		case UNITAI_CITY_COUNTER:
			if (GC.getUnitInfo(eUnit).getCombat() > 0)
			{
				if (!(GC.getUnitInfo(eUnit).isNoDefensiveBonus()))
				{
					if (GC.getUnitInfo(eUnit).getInterceptionProbability() > 0)
					{
						bValid = true;
					}

					if (!bValid)
					{
						for (iI = 0; iI < GC.getNumUnitClassInfos(); iI++)
						{
							if (GC.getUnitInfo(eUnit).getUnitClassDefenseModifier(iI) > 0)
							{
								bValid = true;
								break;
							}
						}
					}

					if (!bValid)
					{
						for (iI = 0; iI < GC.getNumUnitCombatInfos(); iI++)
						{
							if (GC.getUnitInfo(eUnit).getUnitCombatModifier(iI) > 0)
							{
								bValid = true;
								break;
							}
						}
					}
				}
			}
			break;

		case UNITAI_CITY_SPECIAL:
			break;

		case UNITAI_PARADROP:
			if (GC.getUnitInfo(eUnit).getDropRange() > 0)
			{
				bValid = true;
			}
			break;

		case UNITAI_EXPLORE:
			if (GC.getUnitInfo(eUnit).getCombat() > 0)
			{
				if (0 == AI_unitImpassableCount(eUnit))
				{
					bValid = true;
				}
			}
			break;

		case UNITAI_MISSIONARY:
			if (pArea != NULL)
			{
				for (iI = 0; iI < GC.getNumReligionInfos(); iI++)
				{
					if (GC.getUnitInfo(eUnit).getReligionSpreads((ReligionTypes)iI) > 0)
					{
						iNeededMissionaries = AI_neededMissionaries(pArea, ((ReligionTypes)iI));

						if (iNeededMissionaries > 0)
						{
							if (iNeededMissionaries > countReligionSpreadUnits(pArea, ((ReligionTypes)iI)))
							{
								bValid = true;
								break;
							}
						}
					}
				}

				for (iI = 0; iI < GC.getNumCorporationInfos(); iI++)
				{
					if (GC.getUnitInfo(eUnit).getCorporationSpreads((CorporationTypes)iI) > 0)
					{
						iNeededMissionaries = AI_neededExecutives(pArea, ((CorporationTypes)iI));

						if (iNeededMissionaries > 0)
						{
							if (iNeededMissionaries > countCorporationSpreadUnits(pArea, ((CorporationTypes)iI)))
							{
								bValid = true;
								break;
							}
						}
					}
				}
			}
			break;

		case UNITAI_PROPHET:
		case UNITAI_ARTIST:
		case UNITAI_SCIENTIST:
		case UNITAI_GENERAL:
		case UNITAI_MERCHANT:
		case UNITAI_ENGINEER:
		case UNITAI_SPY:
			break;

		case UNITAI_ICBM:
			if (GC.getUnitInfo(eUnit).getNukeRange() != -1)
			{
				bValid = true;
			}
			break;

		case UNITAI_WORKER_SEA:
			for (iI = 0; iI < GC.getNumBuildInfos(); iI++)
			{
				if (GC.getUnitInfo(eUnit).getBuilds(iI))
				{
					bValid = true;
					break;
				}
			}
			break;

		case UNITAI_ATTACK_SEA:
			if (GC.getUnitInfo(eUnit).getCombat() > 0)
			{
				if (GC.getUnitInfo(eUnit).getCargoSpace() == 0)
				{
					if (!(GC.getUnitInfo(eUnit).isInvisible()) && (GC.getUnitInfo(eUnit).getInvisibleType() == NO_INVISIBLE))
					{
						bValid = true;
					}
				}
			}
			break;

		case UNITAI_RESERVE_SEA:
			if (GC.getUnitInfo(eUnit).getCombat() > 0)
			{
				if (GC.getUnitInfo(eUnit).getCargoSpace() == 0)
				{
					bValid = true;
				}
			}
			break;

		case UNITAI_ESCORT_SEA:
			if (GC.getUnitInfo(eUnit).getCombat() > 0)
			{
				if (GC.getUnitInfo(eUnit).getCargoSpace() == 0)
				{
					if (0 == AI_unitImpassableCount(eUnit))
					{
						bValid = true;
					}
				}
			}
			break;

		case UNITAI_EXPLORE_SEA:
			if (GC.getUnitInfo(eUnit).getCargoSpace() <= 1)
			{
				bValid = true;
			}
			break;

		case UNITAI_ASSAULT_SEA:
		case UNITAI_SETTLER_SEA:
			if (GC.getUnitInfo(eUnit).getCargoSpace() > 0)
			{
				if (GC.getUnitInfo(eUnit).getSpecialCargo() == NO_SPECIALUNIT)
				{
					bValid = true;
				}
			}
			break;

		case UNITAI_MISSIONARY_SEA:
		case UNITAI_SPY_SEA:
		case UNITAI_CARRIER_SEA:
		case UNITAI_MISSILE_CARRIER_SEA:
			if (GC.getUnitInfo(eUnit).getCargoSpace() > 0)
			{
				if (GC.getUnitInfo(eUnit).getSpecialCargo() != NO_SPECIALUNIT)
				{
					for (int i = 0; i < NUM_UNITAI_TYPES; ++i)
					{
						if (GC.getSpecialUnitInfo((SpecialUnitTypes)GC.getUnitInfo(eUnit).getSpecialCargo()).isCarrierUnitAIType(eUnitAI))
						{
							bValid = true;
							break;
						}
					}
				}
			}
			break;

		case UNITAI_PIRATE_SEA:
			if (GC.getUnitInfo(eUnit).isAlwaysHostile() && GC.getUnitInfo(eUnit).isHiddenNationality())
			{
				bValid = true;
			}
			break;

		case UNITAI_ATTACK_AIR:
			if (GC.getUnitInfo(eUnit).getAirCombat() > 0)
			{
				if (!GC.getUnitInfo(eUnit).isSuicide())
				{
					bValid = true;
				}
			}
			break;

		case UNITAI_DEFENSE_AIR:
			if (GC.getUnitInfo(eUnit).getInterceptionProbability() > 0)
			{
				bValid = true;
			}
			break;

		case UNITAI_CARRIER_AIR:
			if (GC.getUnitInfo(eUnit).getAirCombat() > 0)
			{
				if (GC.getUnitInfo(eUnit).getInterceptionProbability() > 0)
				{
					bValid = true;
				}
			}
			break;

		case UNITAI_MISSILE_AIR:
			if (GC.getUnitInfo(eUnit).getAirCombat() > 0)
			{
				if (GC.getUnitInfo(eUnit).isSuicide())
				{
					bValid = true;
				}
			}
			break;

		case UNITAI_ATTACK_CITY_LEMMING:
			bValid = false;
			break;

		[COLOR="Red"]/********************************************************************************/
		[COLOR="Blue"]/* LyTning94							2/10/11		*/[/COLOR]
		/* Begin Assassins! Code							*/
		/********************************************************************************/

		case UNITAI_DIPLOMAT:
			break;

		/********************************************************************************/
		/* End Assassins! Code								*/
		/********************************************************************************/[/COLOR]
	
		default:
			[COLOR="Lime"]FAssert(false);[/COLOR]
			break;
		}
	}

	if (!bValid)
	{
		return 0;
	}

	iCombatValue = GC.getGameINLINE().AI_combatValue(eUnit);

	iValue = 1;

	iValue += GC.getUnitInfo(eUnit).getAIWeight();
	
	int iFastMoverMultiplier;

	switch (eUnitAI)
	{
	case UNITAI_UNKNOWN:
	case UNITAI_ANIMAL:
		break;

	case UNITAI_SETTLE:
		iValue += (GC.getUnitInfo(eUnit).getMoves() * 100);
		break;

	case UNITAI_WORKER:
		for (iI = 0; iI < GC.getNumBuildInfos(); iI++)
		{
			if (GC.getUnitInfo(eUnit).getBuilds(iI))
			{
				iValue += 50;
			}
		}
		iValue += (GC.getUnitInfo(eUnit).getMoves() * 100);
		break;

	case UNITAI_ATTACK:
		iFastMoverMultiplier = AI_isDoStrategy(AI_STRATEGY_FASTMOVERS) ? 2 : 1;
		
		iValue += iCombatValue;
		iValue += ((iCombatValue * GC.getUnitInfo(eUnit).getMoves() * iFastMoverMultiplier) / 2);
		iValue += ((iCombatValue * GC.getUnitInfo(eUnit).getWithdrawalProbability()) / 100);
		if (GC.getUnitInfo(eUnit).getCombatLimit() < 100)
		{
			iValue -= (iCombatValue * (125 - GC.getUnitInfo(eUnit).getCombatLimit())) / 100;
		}
		
		break;

	case UNITAI_ATTACK_CITY:
		iFastMoverMultiplier = AI_isDoStrategy(AI_STRATEGY_FASTMOVERS) ? 4 : 1;
		
		iTempValue = ((iCombatValue * iCombatValue) / 75) + (iCombatValue / 2);
		iValue += iTempValue;
		if (GC.getUnitInfo(eUnit).isNoDefensiveBonus())
		{
			iValue -= iTempValue / 2;
		}
		if (GC.getUnitInfo(eUnit).getDropRange() > 0)
		{
			iValue -= iTempValue / 2;
		}
		if (GC.getUnitInfo(eUnit).isFirstStrikeImmune())
		{
			iValue += (iTempValue * 8) / 100;
		}		
		iValue += ((iCombatValue * GC.getUnitInfo(eUnit).getCityAttackModifier()) / 100);
		iValue += ((iCombatValue * GC.getUnitInfo(eUnit).getCollateralDamage()) / 400);
		iValue += ((iCombatValue * GC.getUnitInfo(eUnit).getMoves() * iFastMoverMultiplier) / 4);
		iValue += ((iCombatValue * GC.getUnitInfo(eUnit).getWithdrawalProbability()) / 100);
		if (!AI_isDoStrategy(AI_STRATEGY_AIR_BLITZ))
		{
			int iBombardValue = GC.getUnitInfo(eUnit).getBombardRate() * 4;
			if (iBombardValue > 0)
			{
				//Percentage change
				//Total Bombard == 0 : 600%
				//Total Bombard == 100 : 200%
				//Total Bombard == 200: 100%
				//Total Bombard == 300: 50%
				//Total Bombard == 400: 
				int iTotalBombardRate = AI_calculateTotalBombard(DOMAIN_LAND);
					if (iTotalBombardRate < 100)
					{
					iBombardValue *= 4 * (200 - iTotalBombardRate);
					iBombardValue /= 100;
				}
				else
				{
					iBombardValue *= 100;
					iBombardValue /= std::min(400, iTotalBombardRate);
				}
				iValue += iBombardValue;
			}
		}

		break;

	case UNITAI_COLLATERAL:
		iValue += iCombatValue;
		iValue += ((iCombatValue * GC.getUnitInfo(eUnit).getCollateralDamage()) / 50);
		iValue += ((iCombatValue * GC.getUnitInfo(eUnit).getMoves()) / 4);
		iValue += ((iCombatValue * GC.getUnitInfo(eUnit).getWithdrawalProbability()) / 25);
		iValue -= ((iCombatValue * GC.getUnitInfo(eUnit).getCityAttackModifier()) / 100);
		break;

	case UNITAI_PILLAGE:
		iValue += iCombatValue;
		iValue += (iCombatValue * GC.getUnitInfo(eUnit).getMoves());
		break;

	case UNITAI_RESERVE:
		iValue += iCombatValue;
		iValue -= ((iCombatValue * GC.getUnitInfo(eUnit).getCollateralDamage()) / 200);
		for (iI = 0; iI < GC.getNumUnitCombatInfos(); iI++)
		{
//			int iCombatModifier = GC.getUnitInfo(eUnit).getUnitCombatModifier(iI);
//			iCombatModifier = (iCombatModifier < 40) ? iCombatModifier : (40 + (iCombatModifier - 40) / 2);
//			iValue += ((iCombatValue * iCombatModifier) / 100);
			iValue += ((iCombatValue * GC.getUnitInfo(eUnit).getUnitCombatModifier(iI) * AI_getUnitCombatWeight((UnitCombatTypes)iI)) / 12000);
		}
		iValue += ((iCombatValue * GC.getUnitInfo(eUnit).getMoves()) / 2);
		break;

	case UNITAI_COUNTER:
		iValue += (iCombatValue / 2);
		for (iI = 0; iI < GC.getNumUnitClassInfos(); iI++)
		{
			iValue += ((iCombatValue * GC.getUnitInfo(eUnit).getUnitClassAttackModifier(iI) * AI_getUnitClassWeight((UnitClassTypes)iI)) / 7500);
			iValue += ((iCombatValue * (GC.getUnitInfo(eUnit).getTargetUnitClass(iI) ? 50 : 0)) / 100);
		}
		for (iI = 0; iI < GC.getNumUnitCombatInfos(); iI++)
		{
//			int iCombatModifier = GC.getUnitInfo(eUnit).getUnitCombatModifier(iI);
//			iCombatModifier = (iCombatModifier < 40) ? iCombatModifier : (40 + (iCombatModifier - 40) / 2);
//			iValue += ((iCombatValue * iCombatModifier) / 100);
			iValue += ((iCombatValue * GC.getUnitInfo(eUnit).getUnitCombatModifier(iI) * AI_getUnitCombatWeight((UnitCombatTypes)iI)) / 10000);
			iValue += ((iCombatValue * (GC.getUnitInfo(eUnit).getTargetUnitCombat(iI) ? 50 : 0)) / 100);
		}
		for (iI = 0; iI < GC.getNumUnitInfos(); iI++)
		{
			int eUnitClass = GC.getUnitInfo(eUnit).getUnitClassType();
			if (NO_UNITCLASS != eUnitClass && GC.getUnitInfo((UnitTypes)iI).getDefenderUnitClass(eUnitClass))
			{
				iValue += (50 * iCombatValue) / 100;
			}

			int eUnitCombat = GC.getUnitInfo(eUnit).getUnitCombatType();
			if (NO_UNITCOMBAT != eUnitCombat && GC.getUnitInfo((UnitTypes)iI).getDefenderUnitCombat(eUnitCombat))
			{
				iValue += (50 * iCombatValue) / 100;
			}
		}

		iValue += ((iCombatValue * GC.getUnitInfo(eUnit).getMoves()) / 2);
		iValue += ((iCombatValue * GC.getUnitInfo(eUnit).getWithdrawalProbability()) / 100);
		iValue += (GC.getUnitInfo(eUnit).getInterceptionProbability() * 2);
		break;

	case UNITAI_CITY_DEFENSE:
		iValue += ((iCombatValue * 2) / 3);
		iValue += ((iCombatValue * GC.getUnitInfo(eUnit).getCityDefenseModifier()) / 75);
		break;

	case UNITAI_CITY_COUNTER:
	case UNITAI_CITY_SPECIAL:
	case UNITAI_PARADROP:
		iValue += (iCombatValue / 2);
		iValue += ((iCombatValue * GC.getUnitInfo(eUnit).getCityDefenseModifier()) / 100);
		for (iI = 0; iI < GC.getNumUnitClassInfos(); iI++)
		{
			iValue += ((iCombatValue * GC.getUnitInfo(eUnit).getUnitClassAttackModifier(iI) * AI_getUnitClassWeight((UnitClassTypes)iI)) / 10000);
			iValue += ((iCombatValue * (GC.getUnitInfo(eUnit).getDefenderUnitClass(iI) ? 50 : 0)) / 100);
		}
		for (iI = 0; iI < GC.getNumUnitCombatInfos(); iI++)
		{
			iValue += ((iCombatValue * GC.getUnitInfo(eUnit).getUnitCombatModifier(iI) * AI_getUnitCombatWeight((UnitCombatTypes)iI)) / 10000);
			iValue += ((iCombatValue * (GC.getUnitInfo(eUnit).getDefenderUnitCombat(iI) ? 50 : 0)) / 100);
		}
		iValue += (GC.getUnitInfo(eUnit).getInterceptionProbability() * 3);
		break;

	case UNITAI_EXPLORE:
		iValue += (iCombatValue / 2);
		iValue += (GC.getUnitInfo(eUnit).getMoves() * 200);
		if (GC.getUnitInfo(eUnit).isNoBadGoodies())
		{
			iValue += 100;
		}
		break;

	case UNITAI_MISSIONARY:
		iValue += (GC.getUnitInfo(eUnit).getMoves() * 100);
		if (getStateReligion() != NO_RELIGION)
		{
			if (GC.getUnitInfo(eUnit).getReligionSpreads(getStateReligion()) > 0)
			{
				iValue += (5 * GC.getUnitInfo(eUnit).getReligionSpreads(getStateReligion())) / 2;
			}
		}
		for (iI = 0; iI < GC.getNumReligionInfos(); iI++)
		{
			if (GC.getUnitInfo(eUnit).getReligionSpreads((ReligionTypes)iI) && hasHolyCity((ReligionTypes)iI))
			{
				iValue += 80;
				break;
			}
		}
		if (AI_isDoStrategy(AI_STRATEGY_CULTURE2))
		{
		    int iTempValue = 0;
		    for (iI = 0; iI < GC.getNumReligionInfos(); iI++)
		    {
                if (GC.getUnitInfo(eUnit).getReligionSpreads((ReligionTypes)iI))
                {
                    iTempValue += (50 * getNumCities()) / (1 + getHasReligionCount((ReligionTypes)iI));
                }
		    }
		    iValue += iTempValue;		    
		}
		for (iI = 0; iI < GC.getNumCorporationInfos(); ++iI)
		{
			if (hasHeadquarters((CorporationTypes)iI))
			{
				if (GC.getUnitInfo(eUnit).getCorporationSpreads(iI) > 0)
				{
					iValue += (5 * GC.getUnitInfo(eUnit).getCorporationSpreads(iI)) / 2;
					iValue += 300 / std::max(1, pArea->countHasCorporation((CorporationTypes)iI, getID()));
				}
			}
		}
		break;

	case UNITAI_PROPHET:
	case UNITAI_ARTIST:
	case UNITAI_SCIENTIST:
	case UNITAI_GENERAL:
	case UNITAI_MERCHANT:
	case UNITAI_ENGINEER:
		break;

	case UNITAI_SPY:
		iValue += (GC.getUnitInfo(eUnit).getMoves() * 100);
		if (GC.getUnitInfo(eUnit).isSabotage())
		{
			iValue += 50;
		}
		if (GC.getUnitInfo(eUnit).isDestroy())
		{
			iValue += 50;
		}
		if (GC.getUnitInfo(eUnit).isCounterSpy())
		{
			iValue += 100;
		}
		break;

	case UNITAI_ICBM:
		if (GC.getUnitInfo(eUnit).getNukeRange() != -1)
		{
			iTempValue = 40 + (GC.getUnitInfo(eUnit).getNukeRange() * 40);
			if (GC.getUnitInfo(eUnit).getAirRange() == 0)
			{
				iValue += iTempValue;
			}
			else
			{
				iValue += (iTempValue * std::min(10, GC.getUnitInfo(eUnit).getAirRange())) / 10;
			}
			iValue += (iTempValue * (60 + GC.getUnitInfo(eUnit).getEvasionProbability())) / 100;
		}
		break;

	case UNITAI_WORKER_SEA:
		for (iI = 0; iI < GC.getNumBuildInfos(); iI++)
		{
			if (GC.getUnitInfo(eUnit).getBuilds(iI))
			{
				iValue += 50;
			}
		}
		iValue += (GC.getUnitInfo(eUnit).getMoves() * 100);
		break;

	case UNITAI_ATTACK_SEA:
		iValue += iCombatValue;
		iValue += ((iCombatValue * GC.getUnitInfo(eUnit).getMoves()) / 2);
		iValue += (GC.getUnitInfo(eUnit).getBombardRate() * 4);
		break;

	case UNITAI_RESERVE_SEA:
		iValue += iCombatValue;
		iValue += (iCombatValue * GC.getUnitInfo(eUnit).getMoves());
		break;

	case UNITAI_ESCORT_SEA:
		iValue += iCombatValue;
		iValue += (iCombatValue * GC.getUnitInfo(eUnit).getMoves());
		iValue += (GC.getUnitInfo(eUnit).getInterceptionProbability() * 3);
		if (GC.getUnitInfo(eUnit).getNumSeeInvisibleTypes() > 0)
		{
			iValue += 200;
		}
		break;

	case UNITAI_EXPLORE_SEA:
		{
			int iExploreValue = 100;
			if (pArea != NULL)
			{
			if (pArea->isWater())
			{
				if (pArea->getUnitsPerPlayer(BARBARIAN_PLAYER) > 0)
				{
					iExploreValue += (2 * iCombatValue);
				}
			}
			}
			iValue += (GC.getUnitInfo(eUnit).getMoves() * iExploreValue);
			if (GC.getUnitInfo(eUnit).isAlwaysHostile())
			{
				iValue /= 2;
			}
		iValue /= (1 + AI_unitImpassableCount(eUnit));
		}
		break;

	case UNITAI_ASSAULT_SEA:
	case UNITAI_SETTLER_SEA:
	case UNITAI_MISSIONARY_SEA:
	case UNITAI_SPY_SEA:
		iValue += (iCombatValue / 2);
		iValue += (GC.getUnitInfo(eUnit).getMoves() * 200);
		iValue += (GC.getUnitInfo(eUnit).getCargoSpace() * 300);
		break;

	case UNITAI_CARRIER_SEA:
		iValue += iCombatValue;
		iValue += (GC.getUnitInfo(eUnit).getMoves() * 50);
		iValue += (GC.getUnitInfo(eUnit).getCargoSpace() * 400);
		break;

	case UNITAI_MISSILE_CARRIER_SEA:
		iValue += iCombatValue * GC.getUnitInfo(eUnit).getMoves();
		iValue += (25 + iCombatValue) * (3 + (GC.getUnitInfo(eUnit).getCargoSpace()));
		break;

	case UNITAI_PIRATE_SEA:
		iValue += iCombatValue;
		iValue += (iCombatValue * GC.getUnitInfo(eUnit).getMoves());
		break;

	case UNITAI_ATTACK_AIR:
		iValue += iCombatValue;
		iValue += (GC.getUnitInfo(eUnit).getCollateralDamage() * iCombatValue) / 100;
		iValue += 4 * GC.getUnitInfo(eUnit).getBombRate();
		iValue += (iCombatValue * (100 + 2 * GC.getUnitInfo(eUnit).getCollateralDamage()) * GC.getUnitInfo(eUnit).getAirRange()) / 100;
		break;

	case UNITAI_DEFENSE_AIR:
		iValue += iCombatValue;
		iValue += (GC.getUnitInfo(eUnit).getInterceptionProbability() * 3);
		iValue += (GC.getUnitInfo(eUnit).getAirRange() * iCombatValue);
		break;

	case UNITAI_CARRIER_AIR:
		iValue += (iCombatValue);
		iValue += (GC.getUnitInfo(eUnit).getInterceptionProbability() * 2);
		iValue += (GC.getUnitInfo(eUnit).getAirRange() * iCombatValue);
		break;

	case UNITAI_MISSILE_AIR:
		iValue += iCombatValue;
		iValue += 4 * GC.getUnitInfo(eUnit).getBombRate();
		iValue += GC.getUnitInfo(eUnit).getAirRange() * iCombatValue;
		break;

	case UNITAI_ATTACK_CITY_LEMMING:
		iValue += iCombatValue;
		break;

	[COLOR="Red"]/********************************************************************************/
	/* LyTning94														2/10/11		*/
	/* Begin Assassins! Code														*/
	/********************************************************************************/

	case UNITAI_DIPLOMAT:
		break;

	/********************************************************************************/
	/* End Assassins! Code															*/
	/********************************************************************************/[/COLOR]
	default:
		FAssert(false);
		break;
	}
	
	if ((iCombatValue > 0) && ((eUnitAI == UNITAI_ATTACK) || (eUnitAI == UNITAI_ATTACK_CITY)))
	{
		if (pArea != NULL)
		{
			AreaAITypes eAreaAI = pArea->getAreaAIType(getTeam());
			if (eAreaAI == AREAAI_ASSAULT || eAreaAI == AREAAI_ASSAULT_MASSING)
			{
				for (int iI = 0; iI < GC.getNumPromotionInfos(); iI++)
				{
					if (GC.getUnitInfo(eUnit).getFreePromotions(iI))
					{
						if (GC.getPromotionInfo((PromotionTypes)iI).isAmphib())
						{
							iValue *= 133;
							iValue /= 100;
							break;
						}
					}
				}				
			}
		}
	}

	return std::max(0, iValue);
}

The new code is in red and the assert which fails is in green. The one puzzling aspect is that line 7955 (where the assert supposedly failed) is actually the line I have in blue (which is a comment). If you take away all my added code and comments, however, the FAssert would have been on line 7955 (not sure if this is relevant or not).
 
After changing any .h file, did you delete all the .obj files and rebuild them all? Most of the standard makefiles for civ don't do this. If you are getting line numbers that don't seem to make sense, then it is possible some of your object files are out of date.
 
I use Danny Daemonic's makefile, and I've never had an issue with this in the past, but I'll try it and see. Any idea on my other problems?
 
If you use Danny Daemonic's makefile with fastdep, it should already recompile everything it needs. So rebuilding probably won't solve it, but it's worth a shot.

You could try running the game from within VS, so you can see the code and see what the problem is exactly (use the callstack to see which file is the problem).

Can you post your Civ4UnitAIInfos.xml and the relevant enum from Enums.h?
 
I deleted the .obj files and did a rebuild as davidallen suggested, and while the first assert still fails, the second does not. It still appears to be going in an infinite loop, however. I debugged it to see where exactely this "infinite loop" is, and it appears to be in this function which I created:

Code:
int CvPlayerAI::AI_getBoostDiplomacyAttitude(PlayerTypes ePlayer) const
{
	CvUnit* pOurUnit = NULL;
	CvUnit* pTheirUnit = NULL;
	CvSelectionGroup* pOurGroup;
	CvSelectionGroup* pTheirGroup;
	int iLoop;
	int iAttitude = 0;
	for (pOurUnit = GET_PLAYER(ePlayer).firstUnit(&iLoop); pOurUnit != NULL; pOurUnit = GET_PLAYER(ePlayer).nextUnit(&iLoop))
	{
		pOurGroup = pOurUnit->getGroup();
		if (pOurGroup->headMissionQueueNode() != NULL && getCapitalCity())
		{
			if (pOurGroup->headMissionQueueNode()->m_data.eMissionType == MISSION_DIPLOMACY && pOurGroup->plot() == getCapitalCity()->plot())
			{
				if (pOurUnit->getUnitInfo().getAttitudeIfInBothCapitals())
				{
					for (pTheirUnit = firstUnit(&iLoop); pTheirUnit != NULL; pTheirUnit = nextUnit(&iLoop))
					{
						pTheirGroup = pTheirUnit->getGroup();
						if (pTheirGroup->headMissionQueueNode() != NULL && GET_PLAYER(ePlayer).getCapitalCity())
						{
							if (pTheirGroup->headMissionQueueNode()->m_data.eMissionType == MISSION_DIPLOMACY && pTheirGroup->plot() == GET_PLAYER(ePlayer).getCapitalCity()->plot())
							{
								iAttitude = pOurUnit->getUnitInfo().getAttitudeIfInBothCapitals();								
							}
						}
					}
					if (pOurUnit->getUnitInfo().getAttitudeIfInTheirCapital() > iAttitude)
					{
						iAttitude = pOurUnit->getUnitInfo().getAttitudeIfInTheirCapital();
					}
				}
				else
				{
					iAttitude = pOurUnit->getUnitInfo().getAttitudeIfInTheirCapital();
				}
			}
		}
	}
	if (iAttitude == NULL)
	{
		pTheirUnit = NULL;
		for (pTheirUnit = firstUnit(&iLoop); pTheirUnit != NULL; pTheirUnit = nextUnit(&iLoop))
		{
			pTheirGroup = pTheirUnit->getGroup();
			if (pTheirGroup->headMissionQueueNode() != NULL && GET_PLAYER(ePlayer).getCapitalCity())
			{
				if (pTheirGroup->headMissionQueueNode()->m_data.eMissionType == MISSION_DIPLOMACY && pTheirGroup->plot() == GET_PLAYER(ePlayer).getCapitalCity()->plot())
				{
					iAttitude = pTheirUnit->getUnitInfo().getAttitudeIfInYourCapital();
				}
			}
		}
	}
	return iAttitude;
}

I'm not sure how smart it is to run a firstUnit-nextUnit loop for one player within a firstUnit-nextUnit loop for another player, but this was the only solution my uncreative mind could come up with. Still, I don't see how these loops would cause the game to use 97% of my PC usage for twenty minutes straight before I finally closed the program (especially since there were only around 25 units in the game total when the function was called).

In regards to the assert which still fails, here is my code as requested:

Civ4UnitAIInfos.xml
Code:
<Civ4UnitAIInfos xmlns="x-schema:CIV4BasicInfoSchema.xml">
	<UnitAIInfos>
		<UnitAIInfo>
			<Type>UNITAI_UNKNOWN</Type>
			<Description>UNITAI_UNKNOWN</Description>
		</UnitAIInfo>
		<UnitAIInfo>
			<Type>UNITAI_ANIMAL</Type>
			<Description>UNITAI_ANIMAL</Description>
		</UnitAIInfo>
		<UnitAIInfo>
			<Type>UNITAI_SETTLE</Type>
			<Description>UNITAI_SETTLE</Description>
		</UnitAIInfo>
		<UnitAIInfo>
			<Type>UNITAI_WORKER</Type>
			<Description>UNITAI_WORKER</Description>
		</UnitAIInfo>
		<UnitAIInfo>
			<Type>UNITAI_ATTACK</Type>
			<Description>UNITAI_ATTACK</Description>
		</UnitAIInfo>
		<UnitAIInfo>
			<Type>UNITAI_ATTACK_CITY</Type>
			<Description>UNITAI_ATTACK_CITY</Description>
		</UnitAIInfo>
		<UnitAIInfo>
			<Type>UNITAI_COLLATERAL</Type>
			<Description>UNITAI_COLLATERAL</Description>
		</UnitAIInfo>
		<UnitAIInfo>
			<Type>UNITAI_PILLAGE</Type>
			<Description>UNITAI_PILLAGE</Description>
		</UnitAIInfo>
		<UnitAIInfo>
			<Type>UNITAI_RESERVE</Type>
			<Description>UNITAI_RESERVE</Description>
		</UnitAIInfo>
		<UnitAIInfo>
			<Type>UNITAI_COUNTER</Type>
			<Description>UNITAI_COUNTER</Description>
		</UnitAIInfo>
		<UnitAIInfo>
			<Type>UNITAI_CITY_DEFENSE</Type>
			<Description>UNITAI_CITY_DEFENSE</Description>
		</UnitAIInfo>
		<UnitAIInfo>
			<Type>UNITAI_CITY_COUNTER</Type>
			<Description>UNITAI_CITY_COUNTER</Description>
		</UnitAIInfo>
		<UnitAIInfo>
			<Type>UNITAI_CITY_SPECIAL</Type>
			<Description>UNITAI_CITY_SPECIAL</Description>
		</UnitAIInfo>
		<UnitAIInfo>
			<Type>UNITAI_EXPLORE</Type>
			<Description>UNITAI_EXPLORE</Description>
		</UnitAIInfo>
		<UnitAIInfo>
			<Type>UNITAI_MISSIONARY</Type>
			<Description>UNITAI_MISSIONARY</Description>
		</UnitAIInfo>
		<UnitAIInfo>
			<Type>UNITAI_PROPHET</Type>
			<Description>UNITAI_PROPHET</Description>
		</UnitAIInfo>
		<UnitAIInfo>
			<Type>UNITAI_ARTIST</Type>
			<Description>UNITAI_ARTIST</Description>
		</UnitAIInfo>
		<UnitAIInfo>
			<Type>UNITAI_SCIENTIST</Type>
			<Description>UNITAI_SCIENTIST</Description>
		</UnitAIInfo>
		<UnitAIInfo>
			<Type>UNITAI_GENERAL</Type>
			<Description>UNITAI_GENERAL</Description>
		</UnitAIInfo>
		<UnitAIInfo>
			<Type>UNITAI_MERCHANT</Type>
			<Description>UNITAI_MERCHANT</Description>
		</UnitAIInfo>
		<UnitAIInfo>
			<Type>UNITAI_ENGINEER</Type>
			<Description>UNITAI_ENGINEER</Description>
		</UnitAIInfo>
		<UnitAIInfo>
			<Type>UNITAI_SPY</Type>
			<Description>UNITAI_SPY</Description>
		</UnitAIInfo>
		<UnitAIInfo>
			<Type>UNITAI_ICBM</Type>
			<Description>UNITAI_ICBM</Description>
		</UnitAIInfo>
		<UnitAIInfo>
			<Type>UNITAI_WORKER_SEA</Type>
			<Description>UNITAI_WORKER_SEA</Description>
		</UnitAIInfo>
		<UnitAIInfo>
			<Type>UNITAI_ATTACK_SEA</Type>
			<Description>UNITAI_ATTACK_SEA</Description>
		</UnitAIInfo>
		<UnitAIInfo>
			<Type>UNITAI_RESERVE_SEA</Type>
			<Description>UNITAI_RESERVE_SEA</Description>
		</UnitAIInfo>
		<UnitAIInfo>
			<Type>UNITAI_ESCORT_SEA</Type>
			<Description>UNITAI_ESCORT_SEA</Description>
		</UnitAIInfo>
		<UnitAIInfo>
			<Type>UNITAI_EXPLORE_SEA</Type>
			<Description>UNITAI_EXPLORE_SEA</Description>
		</UnitAIInfo>
		<UnitAIInfo>
			<Type>UNITAI_ASSAULT_SEA</Type>
			<Description>UNITAI_ASSAULT_SEA</Description>
		</UnitAIInfo>
		<UnitAIInfo>
			<Type>UNITAI_SETTLER_SEA</Type>
			<Description>UNITAI_SETTLER_SEA</Description>
		</UnitAIInfo>
		<UnitAIInfo>
			<Type>UNITAI_MISSIONARY_SEA</Type>
			<Description>UNITAI_MISSIONARY_SEA</Description>
		</UnitAIInfo>
		<UnitAIInfo>
			<Type>UNITAI_SPY_SEA</Type>
			<Description>UNITAI_SPY_SEA</Description>
		</UnitAIInfo>
		<UnitAIInfo>
			<Type>UNITAI_CARRIER_SEA</Type>
			<Description>UNITAI_CARRIER_SEA</Description>
		</UnitAIInfo>
		<UnitAIInfo>
			<Type>UNITAI_MISSILE_CARRIER_SEA</Type>
			<Description>UNITAI_MISSILE_CARRIER_SEA</Description>
		</UnitAIInfo>
		<UnitAIInfo>
			<Type>UNITAI_PIRATE_SEA</Type>
			<Description>UNITAI_PIRATE_SEA</Description>
		</UnitAIInfo>
		<UnitAIInfo>
			<Type>UNITAI_ATTACK_AIR</Type>
			<Description>UNITAI_ATTACK_AIR</Description>
		</UnitAIInfo>
		<UnitAIInfo>
			<Type>UNITAI_DEFENSE_AIR</Type>
			<Description>UNITAI_DEFENSE_AIR</Description>
		</UnitAIInfo>
		<UnitAIInfo>
			<Type>UNITAI_CARRIER_AIR</Type>
			<Description>UNITAI_CARRIER_AIR</Description>
		</UnitAIInfo>
		<UnitAIInfo>
			<Type>UNITAI_MISSILE_AIR</Type>
			<Description>UNITAI_MISSILE_AIR</Description>
		</UnitAIInfo>
		<UnitAIInfo>
			<Type>UNITAI_PARADROP</Type>
			<Description>UNITAI_PARADROP</Description>
		</UnitAIInfo>
		<UnitAIInfo>
			<Type>UNITAI_ATTACK_CITY_LEMMING</Type>
			<Description>UNITAI_ATTACK_CITY_LEMMING</Description>
		</UnitAIInfo>
		<UnitAIInfo> <!-- Begin Assassins! Code -->
			<Type>UNITAI_DIPLOMAT</Type>
			<Description>UNITAI_DIPLOMAT</Description>
		</UnitAIInfo> <!-- End Assassins! Code -->
	</UnitAIInfos>
</Civ4UnitAIInfos>

and CvEnums.h
Code:
enum UnitAITypes			// Exposed to Python
{
	NO_UNITAI = -1,

	UNITAI_UNKNOWN,
	UNITAI_ANIMAL,
	UNITAI_SETTLE,
	UNITAI_WORKER,
	UNITAI_ATTACK,
	UNITAI_ATTACK_CITY,
	UNITAI_COLLATERAL,
	UNITAI_PILLAGE,
	UNITAI_RESERVE,
	UNITAI_COUNTER,
	UNITAI_CITY_DEFENSE,
	UNITAI_CITY_COUNTER,
	UNITAI_CITY_SPECIAL,
	UNITAI_EXPLORE,
	UNITAI_MISSIONARY,
	UNITAI_PROPHET,
	UNITAI_ARTIST,
	UNITAI_SCIENTIST,
	UNITAI_GENERAL,
	UNITAI_MERCHANT,
	UNITAI_ENGINEER,
	UNITAI_SPY,
	UNITAI_ICBM,
	UNITAI_WORKER_SEA,
	UNITAI_ATTACK_SEA,
	UNITAI_RESERVE_SEA,
	UNITAI_ESCORT_SEA,
	UNITAI_EXPLORE_SEA,
	UNITAI_ASSAULT_SEA,
	UNITAI_SETTLER_SEA,
	UNITAI_MISSIONARY_SEA,
	UNITAI_SPY_SEA,
	UNITAI_CARRIER_SEA,
	UNITAI_MISSILE_CARRIER_SEA,
	UNITAI_PIRATE_SEA,
	UNITAI_ATTACK_AIR,
	UNITAI_DEFENSE_AIR,
	UNITAI_CARRIER_AIR,
	UNITAI_MISSILE_AIR,
	UNITAI_PARADROP,
	UNITAI_ATTACK_CITY_LEMMING,

	/********************************************************************************/
	/* LyTning94														1/25/11		*/
	/* Begin Assassins! Code														*/
	/********************************************************************************/

	UNITAI_DIPLOMAT,
	
	/********************************************************************************/
	/* End Assassins! Code															*/
	/********************************************************************************/

	NUM_UNITAI_TYPES
};
 
Regarding the assert - try removing the comments in the XML. Although it's considered a correct XML, civ's XML parser sometimes has issues with comments.

Regarding the infinite loop - looking at your code it looks like it's caused by using iLoop for both loops. Since you have a loop inside a loop, you should create two separate variables (e.g. iOurLoop, iTheirLoop).
And not as important - I think you replaced their (for which you use the current player's methods) and ours (for which you use the other player's methods).
 
Thanks for your help, it works perfectly now! I should have realized I couldn't use iLoop for both loops.

I wonder why the XML parser doesn't like comments...
 
Back
Top Bottom