Units upgrades and territory in dll

Alhrath

Warlord
Joined
Jul 18, 2014
Messages
101
Ok, I think I need a little help here ...

I want to give the ability to a specific leader to upgrade his units anywhere on the map, not only on his territory. I started with python, where I found the canUpgradeAnywhere function in CvGameUtils :

Code:
	def canUpgradeAnywhere(self, argsList):
		pUnit = argsList
		
		bCanUpgradeAnywhere = 0
		
		return bCanUpgradeAnywhere

Which seemed to be the exact thing I needed. But all the changes I could make to the function seemed to have no effect. Even when I just made a popup test, it ever showed up. So I thought about going down to the game c++ core to see how the function is handled. Surprise : this function is NEVER called. So I decided to go for some c++ coding. It took me 24h before beeing able to settle an environement in linux (tanks CodeBlocks), but here I am.

So I made some tests, and the command supposed to handle the upgrade capacity is CvUnit::canUpgrade. First I made a call to the python "canUpgradeAnywhere" function that remove the "if (plot()->getTeam() != getTeam())" test if the result is positive. I also added a parameter for the palyer to the argsList so I can later restrict this in python based on the leader.

But I found out it was not enought. I started A LOT of tests to isolate the functions that are blocking the upgrade option to be presented. One way was in the function

Code:
CvCity* CvUnit::getUpgradeCity(UnitTypes eUnit, bool bSearch, int* iSearchValue) const

But even if I bypass it, my units aren't given the possibility to upgrade outside my territory (in it on the contrary, they are given the full possibilities, even in animals), so I guess there is something elsewhere ... but I have no clue where.

So, anybody have an idea ? I suspect something in the functions that create the buttons interface, but i found nothing there linked to anything related to territory yet.
 
So I made some tests, and the command supposed to handle the upgrade capacity is CvUnit::canUpgrade. First I made a call to the python "canUpgradeAnywhere" function that remove the "if (plot()->getTeam() != getTeam())" test if the result is positive. I also added a parameter for the palyer to the argsList so I can later restrict this in python based on the leader.
I think you were on the right track here, can you show us the exact code you wrote?

In general, I wouldn't rely on the Python callback here. These are usually for people who cannot modify the DLL itself, but since you're ready to do that, there's no reason to hold back :)

Something like "if (!isLeader() && plot()->getTeam() != getTeam())" should already work, presuming a CvUnit::isLeader() function exists, not sure right now. Otherwise you might have to check for the promotion itself.

Edit: misread what you wanted to do, thought you wanted to have units with the leader promotion to be able to be upgraded anywhere. Promotions are probably still the most elegant way to go about this (also more transparent to the player). Give your desired leader a trait that gives a new promotion to all units that allows upgrades anywhere. Then check for that promotion in the canUpgrade() method.
 
Here is the code, in CvUnit::isReadyForUpgrade :


Code:
    // ############## MODDED ##################

CyArgsList argsList;
	argsList.add((int) eUnit);
	argsList.add(getOwnerINLINE());
	long lResult=0;

	gDLL->getPythonIFace()->callFunction(PYGameModule, "canUpgradeAnywhere", argsList.makeFunctionArgs(), &lResult);

	if (lResult == 0)
	{
        if (plot()->getTeam() != getTeam())
        {
            return false;
        }
    }

    // END MODDED


I checked, and the lResult is 1, as expected, so the check is skipped. The promotion idea could be nice, I could try to check that. But if I cannot make it hardcoded, I don't know how I could be able to do it the elegent way :p
 
Looks okay then, no idea why it doesn't work. Maybe I can tell you more once I am able to look at the code.
 
Yeah ! It seems it was my bad, I was wrong about "even if I bypass [hasUpgrade function], my units aren't given the possibility to upgrade outside my territory". I checked again and by bypassing it, I get all promotion availible anywhere, even animals and backward upgrades. By the way, it's a fun fact that the AI is able to use that too, and get 1 gunship and 2 mechinised infantry turn 1 with this possibility given :)

So I kept on investigation and I found three interesting functions called :
  • CvCity::canTrain
  • CvPlayer::canTrain
  • CvPlot::canTrain
That are called by the hasUpgrade function. With all that I should be able to mod what I want, I will check that right now.

So meanwhile, let's move to the more elegant way : how to add a new trait and how to check it in dll. Actually I found the function CvPlayer::hasTrait(eTrait), and the xml file CIV4TraitInfos.xml (that's the vanilla one right ? bts doesn't have any), but how do you know the eTrait value from your custom trait ?
 
Here is the code, in CvUnit::isReadyForUpgrade :

I checked, and the lResult is 1, as expected, so the check is skipped. The promotion idea could be nice, I could try to check that. But if I cannot make it hardcoded, I don't know how I could be able to do it the elegent way :p

Does your unit meet all the requirements in CvUnit::canUpgrade ? Does it have movement points left? Does the upgrade require a specific building? If so, does the closest city have that building?

As far as achiveing this functionality using promotions, I did that in my mod last year. Relevant code is here (and a followup bugfix here)
 
It meet every requirements, except the player having at least one city, which is not the case ;) But I managed to find a way, see my post above, I'm working on it right now (I'm adding a special function CvUnit::canTrainSp that recreat all the checks without regard from city or plots, since the city check is quite hardcoded in the process to determine if the upgrade is valid or not).

I'd prefere to use only trait without going threw promotion, as it's suppose to affect all units anywhere, and this player is given units usually, so I'm not sure if the promotion would be given as well in that case.
 
It works ! :)

Here is my new function, that replace CvUnit::hasCityUpgrade when "condition" is checked :

Code:
// ########################### MODDED #########################
// Function to look if unit can by train even without city and without ressource
// Code come from hasCityUpgrade and CvCity::canTrain,

bool CvUnit::canTrainSp(UnitTypes eUnit) const
{
	// START CODE FROM CvUnit::hasCityUpgrade
	if (eUnit == NO_UNIT)
	{
		return false;
	}

	CvPlayerAI& kPlayer = GET_PLAYER(getOwnerINLINE());
	CvUnitInfo& kUnitInfo = GC.getUnitInfo(eUnit);

	if (GC.getCivilizationInfo(kPlayer.getCivilizationType()).getCivilizationUnits(kUnitInfo.getUnitClassType()) != eUnit)
	{

		return false;
	}

	if (!upgradeAvailable(getUnitType(), ((UnitClassTypes)(kUnitInfo.getUnitClassType()))))
	{

		return false;
	}

	if (kUnitInfo.getCargoSpace() < getCargo())
	{

		return false;
	}

	CLLNode<IDInfo>* pUnitNode = plot()->headUnitNode();
	while (pUnitNode != NULL)
	{
		CvUnit* pLoopUnit = ::getUnit(pUnitNode->m_data);
		pUnitNode = plot()->nextUnitNode(pUnitNode);

		if (pLoopUnit->getTransportUnit() == this)
		{
			if (kUnitInfo.getSpecialCargo() != NO_SPECIALUNIT)
			{
				if (kUnitInfo.getSpecialCargo() != pLoopUnit->getSpecialUnitType())
				{
					return false;
				}
			}

			if (kUnitInfo.getDomainCargo() != NO_DOMAIN)
			{
				if (kUnitInfo.getDomainCargo() != pLoopUnit->getDomainType())
				{
					return false;
				}
			}
		}
	}

	// START CODE FROM CvCity::canTrain


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

    return true;

}

Like said, it also disable the check for ressources (which is what I want), and probably also building, religions, etc ... requirement, which I don't care since they are not met in the normal bts game (maybe I'll check that later to make it more accurate on other contexts, like if any ofthese requirements are present, forbit upgrade). Basically, all this is checking is
  • If the player has the tech
  • If the unit can upgrade to said unit
Now I want to work on this "condition", to make it check a trait instead of using a python callback.
 
Hum ... I still can't get how to find the TraitTypes int from a specific trait ...
 
GC.getInfoTypeForString([trait tag]) does not work?
 
Back
Top Bottom