player.canTrain() question

RogerBacon

King
Joined
Nov 16, 2003
Messages
649
In player.canTrain()...

BOOL canTrain(INT iUnit, BOOL bContinue, BOOL bTestVisible)
bool (int eUnit, bool bContinue, bool bTestVisible)

I'm assuming that it should return TRUE if I can build the unit.

Well, I have this code:

Code:
player = gc.getPlayer(pUnit.getOwner())
		pPlayer = gc.getPlayer(player.getID())
		popup.setHeaderString("Insert title here.")
	    	popup.createPullDown()
		for i in range(0 , gc.getNumUnitInfos()):
			if (pPlayer.canTrain(i, true, true)):
				popup.addPullDownString(" %s" %gc.getUnitInfo(i).getDescription() , i)
				message0 = "unit: %s" %gc.getUnitInfo(i).getDescription()
				CvUtil.pyPrint(message0)
		popup.addButton("Ok")

It is returning true for these units:

PY:unit: Settler
PY:unit: Worker
PY:unit: Fast Worker
PY:unit: Catholic Missionary
PY:unit: Protestant Missionary
PY:unit: Orthodox Missionary
PY:unit: Reformed Missionary
PY:unit: Buddhist Missionary
PY:unit: Confucian Missionary
PY:unit: Taoist Missionary
PY:unit: Warrior
PY:unit: Quechua
PY:unit: Chariot
PY:unit: War Chariot
PY:unit: Immortal
PY:unit: Work Boat
PY:unit: Shinto Missionary

The Missionaries are just the vanilla missionaries with a different name. All of the other units are unchanged. This is turn 2 of a game so I don't have the tech to build the missionaries. Also, I'm not playing the civ to have any of those unique units.

Any idea of what could be wrong? Am I making a wrong assumption about what canTrain() actually does? Also, what are the two BOOLs for in the function?

Roger Bacon
 
I'd guess all it's doing is checking CIV4UnitInfos.xml and returning all the units that you can train with your current technology - you could always check the C++ source though to be sure.
 
The Great Apple said:
I'd guess all it's doing is checking CIV4UnitInfos.xml and returning all the units that you can train with your current technology

That's what I'm assuming but then why does it let me build the missionaries? I haven't even discovered any religion yet.

you could always check the C++ source though to be sure.

Any idea which file it is in?

Roger Bacon
 
CvPlayer.cpp of course. It does do a lot of checks, but I don't see it check for prereq buildings at all.

Bh
 
The building check is in CvCity.cpp as is the religion check.

The reason you are seeing missionaries in your return is the CvPlayer version of CanTrain never checks for a required religion (though it does check for a required state religion). I have no idea why they have the 2 checks. You will get better results to your check if you modify the CvPlayer CanTrain to do a CvCity check and return False if the CvCity check comes back False.

Code:
bool CvCity::canTrain(UnitTypes eUnit, bool bContinue, bool bTestVisible)
{
	CvPlot* pLoopPlot;
	SpecialBuildingTypes eSpecialBuilding;
	bool bRequiresBonus;
	bool bNeedsBonus;
	bool bValid;
	int iI;

	if (eUnit == NO_UNIT)
	{
		return false;
	}

	CyCity* pyCity = new CyCity(this);
	CyArgsList argsList;
	argsList.add(gDLL->getPythonIFace()->makePythonObject(pyCity));	// pass in city class
	argsList.add(eUnit);
	argsList.add(bContinue);
	argsList.add(bTestVisible);
	long lResult=0;
	gDLL->getPythonIFace()->callFunction(PYGameModule, "canTrain", argsList.makeFunctionArgs(), &lResult);
	delete pyCity;	// python fxn must not hold on to this pointer
	if (lResult == 1)
	{
		return true;
	}

	if (!(GET_PLAYER(getOwnerINLINE()).canTrain(eUnit, bContinue, bTestVisible)))
	{
		return false;
	}

	if (allUpgradesAvailable(eUnit) != NO_UNIT)
	{
		return false;
	}

	if (GC.getUnitInfo(eUnit).getMinAreaSize() != -1)
	{
		if (GC.getUnitInfo(eUnit).getDomainType() == DOMAIN_SEA)
		{
			bValid = false;

			for (iI = 0; iI < NUM_DIRECTION_TYPES; iI++)
			{
				pLoopPlot = plotDirection(getX_INLINE(), getY_INLINE(), ((DirectionTypes)iI));

				if (pLoopPlot != NULL)
				{
					if (pLoopPlot->isWater())
					{
						if (pLoopPlot->area()->getNumTiles() >= GC.getUnitInfo(eUnit).getMinAreaSize())
						{
							bValid = true;
							break;
						}
					}
				}
			}

			if (!bValid)
			{
				return false;
			}
		}
		else
		{
			if (area()->getNumTiles() < GC.getUnitInfo(eUnit).getMinAreaSize())
			{
				return false;
			}
		}
	}

	if (GC.getUnitInfo(eUnit).isPrereqReligion())
	{
		if (getReligionCount() > 0)
		{
			return false;
		}
	}

	if (GC.getUnitInfo(eUnit).getPrereqReligion() != NO_RELIGION)
	{
		if (!isHasReligion((ReligionTypes)(GC.getUnitInfo(eUnit).getPrereqReligion())))
		{
			return false;
		}
	}

	if (GC.getUnitInfo(eUnit).isPrereqBonuses())
	{
		if (GC.getUnitInfo(eUnit).getDomainType() == DOMAIN_SEA)
		{
			bValid = false;

			for (iI = 0; iI < NUM_DIRECTION_TYPES; iI++)
			{
				pLoopPlot = plotDirection(getX_INLINE(), getY_INLINE(), ((DirectionTypes)iI));

				if (pLoopPlot != NULL)
				{
					if (pLoopPlot->isWater())
					{
						if (pLoopPlot->area()->getNumTotalBonuses() > 0)
						{
							bValid = true;
							break;
						}
					}
				}
			}

			if (!bValid)
			{
				return false;
			}
		}
		else
		{
			if (area()->getNumTotalBonuses() > 0)
			{
				return false;
			}
		}
	}

	if (GC.getUnitInfo(eUnit).getDomainType() == DOMAIN_SEA)
	{
		if (!isCoastal())
		{
			return false;
		}
	}

	if (!bTestVisible)
	{
		if (GC.getUnitInfo(eUnit).getHolyCity() != NO_RELIGION)
		{
			if (!isHolyCity(((ReligionTypes)(GC.getUnitInfo(eUnit).getHolyCity()))))
			{
				return false;
			}
		}

		if (GC.getUnitInfo(eUnit).getPrereqBuilding() != NO_BUILDING)
		{
			if (!hasBuilding((BuildingTypes)(GC.getUnitInfo(eUnit).getPrereqBuilding())))
			{
				eSpecialBuilding = ((SpecialBuildingTypes)(GC.getBuildingInfo((BuildingTypes)(GC.getUnitInfo(eUnit).getPrereqBuilding())).getSpecialBuildingType()));

				if ((eSpecialBuilding == NO_SPECIALBUILDING) || !(GET_PLAYER(getOwnerINLINE()).isSpecialBuildingNotRequired(eSpecialBuilding)))
				{
					return false;
				}
			}
		}

		if (GC.getUnitInfo(eUnit).getPrereqAndBonus() != NO_BONUS)
		{
			if (!hasBonus((BonusTypes)GC.getUnitInfo(eUnit).getPrereqAndBonus()))
			{
				return false;
			}
		}

		bRequiresBonus = false;
		bNeedsBonus = true;

		for (iI = 0; iI < GC.getNUM_UNIT_PREREQ_OR_BONUSES(); iI++)
		{
			if (GC.getUnitInfo(eUnit).getPrereqOrBonuses(iI) != NO_BONUS)
			{
				bRequiresBonus = true;

				if (hasBonus((BonusTypes)GC.getUnitInfo(eUnit).getPrereqOrBonuses(iI)))
				{
					bNeedsBonus = false;
				}
			}
		}

		if (bRequiresBonus && bNeedsBonus)
		{
			return false;
		}
	}

	pyCity = new CyCity(this);
	CyArgsList argsList2; // XXX
	argsList2.add(gDLL->getPythonIFace()->makePythonObject(pyCity));	// pass in city class
	argsList2.add(eUnit);
	argsList2.add(bContinue);
	argsList2.add(bTestVisible);
	lResult=0;
	gDLL->getPythonIFace()->callFunction(PYGameModule, "cannotTrain", argsList2.makeFunctionArgs(), &lResult);
	delete pyCity;	// python fxn must not hold on to this pointer
	if (lResult == 1)
	{
		return false;
	}

	return true;
}
 
RogerBacon said:
In player.canTrain()...

Also, what are the two BOOLs for in the function?

Roger Bacon

BTW, the bools are (out of my experiences) :

bContinue :
when you check if you can continue production of such a unit. But I'm not 100% sure about this. I think you can only filter out continuable units.

bTestVisible :
use to check if you are able to produce such a unit. The visibility and the status of the unit production button in the main interface is controlled with it.

bTestVisibility = TRUE -> returns if the button is displayed or not. In other words, if you do have the technological prerequisites for that unit or not. Controls if the button in the main interface is displayed or not.

bTestVisiblity = FALSE -> if you are able to produce that unit. In other words, if you do the city and financial prerequisites or not (gold, buildings, ...) Controls if the button in the main interface is enabled or not.

So, if you wonder about the function fails or your mod, you may compare the results with the buttons of the standard missionaries in the main interface. If they are displayed although you haven't all prereqs, its very likly the function. If not, its probably your code/XML.

12m
 
Thank you everyone.

Kael, in the code you posted the player version of canTrain does a city.canTrain check, right? Does it just create a generic city? If so, it would always return false for missionaries sicne the city wouldn't have any religion. Is that correct?

Roger Bacon
 
RogerBacon said:
Thank you everyone.

Kael, in the code you posted the player version of canTrain does a city.canTrain check, right? Does it just create a generic city? If so, it would always return false for missionaries sicne the city wouldn't have any religion. Is that correct?

Roger Bacon

The player version of canTrain doesn't do a city check. Looking at it more closely the player canTrain isn't passed the city so it wouldnt be possible (or would be very difficult) to pass the player.canTrain to it.

If you can to key off of a canTrain python routine you will probably want to use the canTrain in CvGameInterface.py which is passed the city and should be a more realistic view of what can be built.
 
The player version doesn't, but it seems like the city version does. At least, I'm pretty sure that's what this line is:

if (!(GET_PLAYER(getOwnerINLINE()).canTrain(eUnit, bContinue, bTestVisible)))

Bh
 
I added city.canTrain(i), using the capital city as the city in question, and that got rid of the missionaries but the UU are still there. Incidentally, I first tried city.canTrain(i, true, true) but I got an error saying I used 4 arguments and canTrain() only takes 2 arguments.
Anyone know of a way to get the UU out of the list?

Roger Bacon
 
RogerBacon said:
I added city.canTrain(i), using the capital city as the city in question, and that got rid of the missionaries but the UU are still there. Incidentally, I first tried city.canTrain(i, true, true) but I got an error saying I used 4 arguments and canTrain() only takes 2 arguments.
Anyone know of a way to get the UU out of the list?

Roger Bacon

I assume that you can match the unit index against CvUnitClassInfo getDefaultUnitIndex() and if it matches then its not a UU.
 
Back
Top Bottom