Case Study: Adding new XML attributes and using them in the SDK

I think I know where I went wrong. I went straight in and modified the GameCore, wheras-judging from your Tutorial-I should have added the new attributes to the XML file first.

Aussie_Lurker.
 
Sorry if this is a stupid question or if it doesn't really belong in this thread. Can we read in new XML values through Python too or do we have to go through the SDK? If we can do it in Python, do we use DOM or SAX (which I've heard of but don't know much about), or is there something better already set up for us?

Thanks a lot for any help you can provide.
 
Raumohir said:
Sorry if this is a stupid question or if it doesn't really belong in this thread. Can we read in new XML values through Python too or do we have to go through the SDK? If we can do it in Python, do we use DOM or SAX (which I've heard of but don't know much about), or is there something better already set up for us?

Thanks a lot for any help you can provide.

No, there are python interfaces to the info files. Check out the three files CyInfoInterface1-3.cpp. For each type of info class, it has a python interface. Basically, each one creates a python function and sets up that python function to call the correct c++ function. For example, in the CvUnitInfo class:

Code:
python::class_<CvCivilizationInfo, python::bases<CvInfoBase> >("CvCivilizationInfo")
		.def("getDefaultPlayerColor", &CvCivilizationInfo::getDefaultPlayerColor, "int ()")
		.def("getArtStyleType", &CvCivilizationInfo::getArtStyleType, "int ()")
		.def("getNumCityNames", &CvCivilizationInfo::getNumCityNames, "int ()")
		.def("getNumLeaders", &CvCivilizationInfo::getNumLeaders, "int ()")

In order to add new functions to the CvUnitInfo, you would do everything that Kael has listed, plus add a new .def line. For the arguments:

1.) The name of the function that will be seen in python, as a string.
2.) The reference to the function to be called.
3.) The string that shows the fingerprint of the function.

Basically, just take a look at how other functions are shown here and try to emulate them. You would call the getNumLeaders function in python by doing:

Code:
gc.getCivilizationInfo(iNum).getNumLeaders()

So, anything you add to this file, after compiling, will allow you to call it by doing...

Code:
gc.getCivilizationInfo(iNum).yourFunctionHere()

Pretty quick writeup, so if you need more help just reply.

Edit: This thread has a pretty good writeup for that too.
 
How do you add multiple lines tag in XML. By that I mean something like:
Code:
			<UnitClassUpgrades>
				<UnitClassUpgrade>
					<UnitClassUpgradeType>UNITCLASS_KNIGHT</UnitClassUpgradeType>
					<bUnitClassUpgrade>1</bUnitClassUpgrade>
				</UnitClassUpgrade>
			</UnitClassUpgrades>
 
Kael, thanks for the wonderful tutorial. I was able to follow it and successfully add a function that gave the player a diplomatic bonus when using the Peacekeeping civic (see this thread). However, I am getting the bonus twice: the left-hand side of the diplpmacy screen (where it lsits bonuses and penalties) shows "+4 You help with peacekeeping operations" AND "+1 you help with peacekeeping operations", where I only wanted the +4 bonus.

I suspect that this is because of this peace of code:

CvPlayerAI.cpp

Code:
int CvPlayerAI::AI_getHighCompassionAttitude(PlayerTypes ePlayer)
{
	int iAttitude;

	iAttitude = 0;

	if (GET_PLAYER(ePlayer).getCivics((CivicOptionTypes)GC.getInfoTypeForString("CIVICOPTION_COMPASSION")) == (CivicOptionTypes)GC.getInfoTypeForString("CIVIC_PUBLIC_HEALERS") ||
        GET_PLAYER(ePlayer).getCivics((CivicOptionTypes)GC.getInfoTypeForString("CIVICOPTION_COMPASSION")) == (CivicOptionTypes)GC.getInfoTypeForString("CIVIC_PROTECT_THE_MEET"))
	{
		iAttitude += GC.getLeaderHeadInfo(getPersonalityType()).getHighCompassionAttitudeChange();
	}

	return iAttitude;
}

which I changed to:

Code:
int CvPlayerAI::AI_get[b]Peacekeeping[/b]Attitude(PlayerTypes ePlayer)
{
	int iAttitude;

	iAttitude = 0;

	if (GET_PLAYER(ePlayer).getCivics((CivicOptionTypes)GC.getInfoTypeForString("CIVICOPTION_[b]WARFARE[/b]")) == (CivicOptionTypes)GC.getInfoTypeForString("CIVIC_[b]PEACEKEEPING[/b]") ||
        GET_PLAYER(ePlayer).getCivics((CivicOptionTypes)GC.getInfoTypeForString("CIVICOPTION_[b]WARFARE[/b]")) == (CivicOptionTypes)GC.getInfoTypeForString("CIVIC_[b]PEACKEEPING[/b]"))
	{
		iAttitude += GC.getLeaderHeadInfo(getPersonalityType()).getHighCompassionAttitudeChange();
	}

	return iAttitude;
}

Any thoughts?
 
zulu9812 said:
Kael, thanks for the wonderful tutorial. I was able to follow it and successfully add a function that gave the player a diplomatic bonus when using the Peacekeeping civic (see this thread). However, I am getting the bonus twice: the left-hand side of the diplpmacy screen (where it lsits bonuses and penalties) shows "+4 You help with peacekeeping operations" AND "+1 you help with peacekeeping operations", where I only wanted the +4 bonus.

I suspect that this is because of this peace of code:

First off you can change that code to the following, its actually chekcing for the same thing twice in your example. This will clean it up a bit:

Code:
int CvPlayerAI::AI_get[b]Peacekeeping[/b]Attitude(PlayerTypes ePlayer)
{
	int iAttitude;

	iAttitude = 0;

	if (GET_PLAYER(ePlayer).getCivics((CivicOptionTypes)GC.getInfoTypeForString("CIVICOPTION_[b]WARFARE[/b]")) == (CivicOptionTypes)GC.getInfoTypeForString("CIVIC_[b]PEACEKEEPING[/b]"))
	{
		iAttitude += GC.getLeaderHeadInfo(getPersonalityType()).getHighCompassionAttitudeChange();
	}

	return iAttitude;
}

But I dont think that is your problem. Can you attach your CvPlayerAI.cpp file?
 
Its probably something on the GameTextManager.cpp. Search that for all references to the Text Tag and you will likly find two places ware its being parsed and put in the Diplomacy modifiers box. AI.cpp files just affect what the AI thinks of you they have nothing to do with Text being displayed.
 
Lord Olleus said:
How do you add multiple lines tag in XML. By that I mean something like:
Code:
			<UnitClassUpgrades>
				<UnitClassUpgrade>
					<UnitClassUpgradeType>UNITCLASS_KNIGHT</UnitClassUpgradeType>
					<bUnitClassUpgrade>1</bUnitClassUpgrade>
				</UnitClassUpgrade>
			</UnitClassUpgrades>


In this case, there is one element (UnitClassUpgrades) that contain multiple smaller elements, all that have a pair of items (each UnitClassUpgrade contains a "UnitClassUpgradeType" and a "bUnitClassUpgrade". The latter two are considered a "pair"). What you do is make an array (check out the variable m_pbUpgradeUnitClass to see how they do it). Since the value for each type is boolean, it would be a boolean array. Aside from making sure to add it in to all the other places as the tutorial suggests (although making sure that you make it look like the other arrays, with proper SAFE_DELETE_ARRAY calls where needed), you can then pass in that array and the other arguments to this function:

Code:
pXML->SetVariableListTagPair(&m_pbUpgradeUnitClass, "UnitClassUpgrades", GC.getUnitClassInfo(), sizeof(GC.getUnitClassInfo((UnitClassTypes)0)), GC.getNumUnitClassInfos());

The array will already be initialized to the default false value for every type (in this case, UnitClassType). It then goes through the "List" of pairs, and for every one reads it's value, and then stores the second value in the array's index for that type. So, say you have UNITCLASS_LION, which has an info index of 0. If you set true in that list, then the index 0 of the array used will be set to true.

If you want to make a similar list, but use more than a Pair, it would be a bit more tricky, since I don't think there's a built-in call.

I'm not sure if that explains it adequetly, but if you look at the code you'll see that anytime you have a list like that, there is an array of length equal to the number of types of infos for the first part, and the value being the default or the value you set in the second part of each "pair".
 
90% of what you might want to do can be stolen from existing code with a copy/paste and change of variable names.

Be aware that the order inwhich files are loaded on the XMLloadutility.cpp will put limits on what can reference what, for example Techs cant referance many things because their loaded very early.

Its possible to create new and unusual nested elements that have no equivilent to copy from but you have to have a good understanding of how the XML read functions and XML tree navigation functions work. I managed to create a triple nesteded element containing 2 building references at the bottom for a building modifying mod and though it was tricky I did get it working. (unfortunatly I abandoned that mod for an even more ambitious version, darn feature creep :rolleyes: )

I'm next going to try to create a whole new XML document and see if I can get it loaded.
 
Impaler[WrG] said:
90% of what you might want to do can be stolen from existing code with a copy/paste and change of variable names.

Be aware that the order inwhich files are loaded on the XMLloadutility.cpp will put limits on what can reference what, for example Techs cant referance many things because their loaded very early.

Its possible to create new and unusual nested elements that have no equivilent to copy from but you have to have a good understanding of how the XML read functions and XML tree navigation functions work. I managed to create a triple nesteded element containing 2 building references at the bottom for a building modifying mod and though it was tricky I did get it working. (unfortunatly I abandoned that mod for an even more ambitious version, darn feature creep :rolleyes: )

I'm next going to try to create a whole new XML document and see if I can get it loaded.

Feel free to check out FfH2, we have a new xml document 'CIV4SpellInfos.xml' that we use and it works great.
 
Kael said:
But I dont think that is your problem. Can you attach your CvPlayerAI.cpp file?

I think that I might have figured out why I'm getting the same bonus twice (although not how to fix it). I decided to just start again from scratch, and this time I missed out a step. Whereas before I had gone through every leaderhead and told it to give +4 favour if the other civ had Peacekeeping, I forgot to do that this time. Upon testing in game, I was getting only the +1 bonus (if you'll recall, in the previous incarnation, I had been getting +4 AND +1). Thus, we can surmise that there is something in the SDK code that is producing this +1 bonus, independent of what the XML is doing.

Anyway, I've uploaded all the SDK files that I modified. I'd appreciate some advice.

http://www.civfanatics.net/uploads12/CvGameCoreDLL.zip
 
Your code looks right to me. From what you are describing the +1 isn't coming from your new attitude variable. Is it possible it is coming from the AI_getPeaceAttitude value?

What is the TXT_KEY_MISC_ATTITUDE_PEACE set to in your mod?
 
Kael said:
Your code looks right to me. From what you are describing the +1 isn't coming from your new attitude variable. Is it possible it is coming from the AI_getPeaceAttitude value?

Well, before, the diplomacy screen would say "+1 You help with peacekeeping operations" and "+4 You help with peacekeeping operations". Now, because I left you that bonus from the Civ4LeaderheadInfos.xml file, it is just saying "+1 You help with peacekeeping operations" - when it should be saying neither. I'm not sure why it would be related to the getPeaceAttitude value.

Kael said:
What is the TXT_KEY_MISC_ATTITUDE_PEACE set to in your mod?

You mean in the XML file in the Text folder? If so, it's

"Years of peace have strengthened our relations"
 
I woudl still recommend cleaning up your AI_getPeacekeepingAttitude as follows:

Code:
int CvPlayerAI::AI_getPeacekeepingAttitude(PlayerTypes ePlayer)
{
	int iAttitude;

	iAttitude = 0;

	if (GET_PLAYER(ePlayer).getCivics((CivicOptionTypes)GC.getInfoTypeForString("CIVICOPTION_WARFARE")) == (CivicOptionTypes)GC.getInfoTypeForString("CIVIC_PEACEKEEPING"))
	{
		iAttitude += GC.getLeaderHeadInfo(getPersonalityType()).getPeacekeepingAttitudeChange();
	}

	return iAttitude;
}

Ouside of that it all looks right to me (though like you say you are going to need the xml values set correctly in the leaderhead file).
 
Kael said:
I woudl still recommend cleaning up your AI_getPeacekeepingAttitude as follows:

Thanks for your help. I did that, but it made no difference. The "+1 You help with peacekeeping operations" only appears when I have the Peacekeeping civic enacted, which makes me think that it is an issue with the SDK code, rather than being related to the AI_getPeaceAttitude value
 
zulu9812 said:
Thanks for your help. I did that, but it made no difference. The "+1 You help with peacekeeping operations" only appears when I have the Peacekeeping civic enacted, which makes me think that it is an issue with the SDK code, rather than being related to the AI_getPeaceAttitude value

Oh, does the other civ have it as his favorite civic?
 
Just thought I'd give this a little bumo, to see if anyone can help. I'm anxious to know that I can get this right, as it would mean I could move on to other projects with confidence,
 
Try what I said, search for the TAG of your new "you help with peacekeeping" text, It sounds like you changed an existing Text element rather then creating a new one (thats what Kael was getting at with his earlier question). Any new text needs to have a new UNIQUE tag for it to get show in the game.

Find what tag the new text uses and search for that under the TextManager, if their is only one instance of it being used you know THAT is the code doing it. Its going to be under one of the ParseDiplomacytext functions, and its aparently being called with a value of +1.
 
No, I don't think that's it. I checked over the CvGameTextMgr.cpp, and there's only one section that's different to the default and that's the section that I added (between the bit for bonus about having different religion and the trading bonus). If you'd like to check that I've done it correctly (which I think I have) all the files that I modified are in this zip:

http://www.civfanatics.net/uploads12/CvGameCoreDLL.zip
 
Top Bottom