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


Kael
Apr 13, 2006, 10:38 AM
My goal in making this was to add 4 new values to leaders that adjusts their attitude. Those values are:

1. An attitude modifier based on the other civilizations alignment compared to the deciding civilization.
2. An attitude modifier if the other civilization uses Death magic.
3. An attitude modifier if the other civilization uses Entropy magic.
4. An attitude modifier if the other civilization has its Compassion civic option set to a "good" value.
5. An attitude modifier if the other civilization has its Compassion civic option set to an "evil" value.

I realize these are FfH specific values but I tihnk the process is general enough that it can be used for other functions. Basically for anytime you want to create a new value in XML and reference it within the SDK.

Step 1: Add the values to the XML

First the CIV4CivilizationsSchema.xml file will need to be modified to allow the new attributes. The new attribute definitions were inserted into the following section. The new lines are in bold.

<ElementType name="iSameReligionAttitudeChange" content="textOnly" dt:type="int"/>
<ElementType name="iSameReligionAttitudeDivisor" content="textOnly" dt:type="int"/>
<ElementType name="iSameReligionAttitudeChangeLimit" content="textOnly" dt:type="int"/>
<ElementType name="iDifferentReligionAttitudeChange" content="textOnly" dt:type="int"/>
<ElementType name="iDifferentReligionAttitudeDivisor" content="textOnly" dt:type="int"/>
<ElementType name="iDifferentReligionAttitudeChangeLimit" content="textOnly" dt:type="int"/>
<ElementType name="Alignment" content="textOnly" dt:type="int"/>
<ElementType name="iUseDeathAttitudeChange" content="textOnly" dt:type="int"/>
<ElementType name="iUseEntropyAttitudeChange" content="textOnly" dt:type="int"/>
<ElementType name="iHighCompassionAttitudeChange" content="textOnly" dt:type="int"/>
<ElementType name="iLowCompassionAttitudeChange" content="textOnly" dt:type="int"/>
<ElementType name="iBonusTradeAttitudeDivisor" content="textOnly" dt:type="int"/>
<ElementType name="iBonusTradeAttitudeChangeLimit" content="textOnly" dt:type="int"/>

In the <ElementType name="LeaderHeadInfo" content="eltOnly"> section I added the definitions to make them valid attributes for the LeaderHeadInfo element (changes in bold):

<element type="iSameReligionAttitudeChange"/>
<element type="iSameReligionAttitudeDivisor"/>
<element type="iSameReligionAttitudeChangeLimit"/>
<element type="iDifferentReligionAttitudeChange"/>
<element type="iDifferentReligionAttitudeDivisor"/>
<element type="iDifferentReligionAttitudeChangeLimit"/>
<element type="Alignment"/>
<element type="iUseDeathAttitudeChange"/>
<element type="iUseEntropyAttitudeChange"/>
<element type="iHighCompassionAttitudeChange"/>
<element type="iLowCompassionAttitudeChange"/>
<element type="iBonusTradeAttitudeDivisor"/>
<element type="iBonusTradeAttitudeChangeLimit"/>

Then in the CIV4LeaderHeadInfos.xml file each leader must be modified to insert the new attributes and give each a value.

<iSameReligionAttitudeChange>1</iSameReligionAttitudeChange>
<iSameReligionAttitudeDivisor>10</iSameReligionAttitudeDivisor>
<iSameReligionAttitudeChangeLimit>3</iSameReligionAttitudeChangeLimit>
<iDifferentReligionAttitudeChange>-1</iDifferentReligionAttitudeChange>
<iDifferentReligionAttitudeDivisor>-5</iDifferentReligionAttitudeDivisor>
<iDifferentReligionAttitudeChangeLimit>-1</iDifferentReligionAttitudeChangeLimit>
<Alignment>1</Alignment>
<iUseDeathAttitudeChange>-4</iUseDeathAttitudeChange>
<iUseEntropyAttitudeChange>-4</iUseEntropyAttitudeChange>
<iHighCompassionAttitudeChange>1</iHighCompassionAttitudeChange>
<iLowCompassionAttitudeChange>-1</iLowCompassionAttitudeChange>
<iBonusTradeAttitudeDivisor>50</iBonusTradeAttitudeDivisor>
<iBonusTradeAttitudeChangeLimit>2</iBonusTradeAttitudeChangeLimit>

Once we are done these values will give this leader a -4 attitude modifier if the other civ is unsing death magic, -4 if the other civ is using entropy magic, +1 if the other civ has a "good" option selected in their Compassion civic and -1 if the other civ has an "evil" option selected in their Compassion civic.

This is a good point to stop and load your mod. It should load without errors if everything to this point has been done correctly. But since we haven't changed any code to take advantage of the attributes even if it does load without errors the new attributes won't do anything. So off we go to the SDK.

Step 2: Allowing the SDK to read the new attributes

The next step is to expose our new XML attributes to the SDK. For that we will need to define some functions that will read in the new attributes. This is done in 2 modules. In the CvInfo.cpp we will need to make the following changes:

CvInfos.cpp

First we need to define the internal variables we will use:

m_iSameReligionAttitudeChange(0),
m_iSameReligionAttitudeDivisor(0),
m_iSameReligionAttitudeChangeLimit(0),
m_iDifferentReligionAttitudeChange(0),
m_iDifferentReligionAttitudeDivisor(0),
m_iDifferentReligionAttitudeChangeLimit(0),
m_iAlignment(0),
m_iUseDeathAttitudeChange(0),
m_iUseEntropyAttitudeChange(0),
m_iHighCompassionAttitudeChange(0),
m_iLowCompassionAttitudeChange(0),
m_iBonusTradeAttitudeDivisor(0),
m_iBonusTradeAttitudeChangeLimit(0),

Then (also in CvInfos.cpp) we must add the functions that allow us to query this attribute.

int CvLeaderHeadInfo::getSameReligionAttitudeChange() const
{
return m_iSameReligionAttitudeChange;
}

int CvLeaderHeadInfo::getSameReligionAttitudeDivisor() const
{
return m_iSameReligionAttitudeDivisor;
}

int CvLeaderHeadInfo::getSameReligionAttitudeChangeLim it() const
{
return m_iSameReligionAttitudeChangeLimit;
}

int CvLeaderHeadInfo::getDifferentReligionAttitudeChan ge() const
{
return m_iDifferentReligionAttitudeChange;
}

int CvLeaderHeadInfo::getDifferentReligionAttitudeDivi sor() const
{
return m_iDifferentReligionAttitudeDivisor;
}

int CvLeaderHeadInfo::getDifferentReligionAttitudeChan geLimit() const
{
return m_iDifferentReligionAttitudeChangeLimit;
}

int CvLeaderHeadInfo::getAlignment() const
{
return m_iAlignment;
}

void CvLeaderHeadInfo::setAlignment(int i)
{
m_iAlignment = i;
}

int CvLeaderHeadInfo::getUseDeathAttitudeChange() const
{
return m_iUseDeathAttitudeChange;
}

int CvLeaderHeadInfo::getUseEntropyAttitudeChange() const
{
return m_iUseEntropyAttitudeChange;
}

int CvLeaderHeadInfo::getHighCompassionAttitudeChange( ) const
{
return m_iHighCompassionAttitudeChange;
}

int CvLeaderHeadInfo::getLowCompassionAttitudeChange() const
{
return m_iLowCompassionAttitudeChange;
}

int CvLeaderHeadInfo::getBonusTradeAttitudeDivisor() const
{
return m_iBonusTradeAttitudeDivisor;
}

int CvLeaderHeadInfo::getBonusTradeAttitudeChangeLimit () const
{
return m_iBonusTradeAttitudeChangeLimit;
}

Then in the CvLeaderHeadInfo::read(FDataStreamBase* stream) function we will add the ability to read the new attributes.

stream->Read(&m_iSameReligionAttitudeChange);
stream->Read(&m_iSameReligionAttitudeDivisor);
stream->Read(&m_iSameReligionAttitudeChangeLimit);
stream->Read(&m_iDifferentReligionAttitudeChange);
stream->Read(&m_iDifferentReligionAttitudeDivisor);
stream->Read(&m_iDifferentReligionAttitudeChangeLimit);
stream->Read(&m_iAlignment);
stream->Read(&m_iUseDeathAttitudeChange);
stream->Read(&m_iUseEntropyAttitudeChange);
stream->Read(&m_iHighCompassionAttitudeChange);
stream->Read(&m_iLowCompassionAttitudeChange);
stream->Read(&m_iBonusTradeAttitudeDivisor);
stream->Read(&m_iBonusTradeAttitudeChangeLimit);

Likewise in the CvLeaderHeadInfo::write(FDataStreamBase* stream) function we will need to add the ability to write to the new attributes.

stream->Write(m_iSameReligionAttitudeChange);
stream->Write(m_iSameReligionAttitudeDivisor);
stream->Write(m_iSameReligionAttitudeChangeLimit);
stream->Write(m_iDifferentReligionAttitudeChange);
stream->Write(m_iDifferentReligionAttitudeDivisor);
stream->Write(m_iDifferentReligionAttitudeChangeLimit);
stream->Write(m_iAlignment);
stream->Write(m_iUseDeathAttitudeChange);
stream->Write(m_iUseEntropyAttitudeChange);
stream->Write(m_iHighCompassionAttitudeChange);
stream->Write(m_iLowCompassionAttitudeChange);
stream->Write(m_iBonusTradeAttitudeDivisor);
stream->Write(m_iBonusTradeAttitudeChangeLimit);

Lastly (for CvInfos.cpp) we will need the ability to pull the attribute from XML. This is done in the CvLeaderHeadInfo::read(CvXMLLoadUtility* pXML) function.

pXML->GetChildXmlValByName(&m_iSameReligionAttitudeChange, "iSameReligionAttitudeChange");
pXML->GetChildXmlValByName(&m_iSameReligionAttitudeDivisor, "iSameReligionAttitudeDivisor");
pXML->GetChildXmlValByName(&m_iSameReligionAttitudeChangeLimit, "iSameReligionAttitudeChangeLimit");
pXML->GetChildXmlValByName(&m_iDifferentReligionAttitudeChange, "iDifferentReligionAttitudeChange");
pXML->GetChildXmlValByName(&m_iDifferentReligionAttitudeDivisor, "iDifferentReligionAttitudeDivisor");
pXML->GetChildXmlValByName(&m_iDifferentReligionAttitudeChangeLimit, "iDifferentReligionAttitudeChangeLimit");
pXML->GetChildXmlValByName(&m_iAlignment, "Alignment");
pXML->GetChildXmlValByName(&m_iUseDeathAttitudeChange, "iUseDeathAttitudeChange");
pXML->GetChildXmlValByName(&m_iUseEntropyAttitudeChange, "iUseEntropyAttitudeChange");
pXML->GetChildXmlValByName(&m_iHighCompassionAttitudeChange, "iHighCompassionAttitudeChange");
pXML->GetChildXmlValByName(&m_iLowCompassionAttitudeChange, "iLowCompassionAttitudeChange");
pXML->GetChildXmlValByName(&m_iBonusTradeAttitudeDivisor, "iBonusTradeAttitudeDivisor");
pXML->GetChildXmlValByName(&m_iBonusTradeAttitudeChangeLimit, "iBonusTradeAttitudeChangeLimit");

Then onto CvInfos.h to declare the new functions we added.

CvInfos.h

DllExport int getSameReligionAttitudeChange() const; // Exposed to Python
DllExport int getSameReligionAttitudeDivisor() const; // Exposed to Python
DllExport int getSameReligionAttitudeChangeLimit() const; // Exposed to Python
DllExport int getDifferentReligionAttitudeChange() const; // Exposed to Python
DllExport int getDifferentReligionAttitudeDivisor() const; // Exposed to Python
DllExport int getDifferentReligionAttitudeChangeLimit() const; // Exposed to Python
DllExport int getAlignment() const; // Exposed to Python
DllExport void setAlignment(int i); // Exposed to Python
DllExport int getUseDeathAttitudeChange() const; // Exposed to Python
DllExport int getUseEntropyAttitudeChange() const; // Exposed to Python
DllExport int getHighCompassionAttitudeChange() const; // Exposed to Python
DllExport int getLowCompassionAttitudeChange() const; // Exposed to Python
DllExport int getBonusTradeAttitudeDivisor() const; // Exposed to Python
DllExport int getBonusTradeAttitudeChangeLimit() const; // Exposed to Python

And the new variables we added.

int m_iSameReligionAttitudeChange;
int m_iSameReligionAttitudeDivisor;
int m_iSameReligionAttitudeChangeLimit;
int m_iDifferentReligionAttitudeChange;
int m_iDifferentReligionAttitudeDivisor;
int m_iDifferentReligionAttitudeChangeLimit;
int m_iAlignment;
int m_iUseDeathAttitudeChange;
int m_iUseEntropyAttitudeChange;
int m_iHighCompassionAttitudeChange;
int m_iLowCompassionAttitudeChange;
int m_iBonusTradeAttitudeDivisor;
int m_iBonusTradeAttitudeChangeLimit;


At this point we have new attributes in XML and the ability to read them in the game engine. But we still don't actually do anything with them. go ahead and compile your DLL with just these changes and make sure it loads without errors before going onto the next section.

Step 3: Making the new attributes do something

Now the fun part, we get to do something with our new attributes! This section will probably be less specifically applicable to your mod because it details exactly how I will be using these attributes, you may use the attributes you created entirely differently. Still it serves as a good example of what can be done.

The attitude modifiers are handled in the CvPlayerAI.cpp file so we will create 4 new functions in that file. As you can tell from the code this is where we really define what it means to "UseDeath" or "HighCompassion". Basically a civilization applies its iUseDeathAttitudeChange modifier if the civilization it is judging has the death mana resource in its capital. If we wanted to change this definition (to look through all the cities, see if "death" units have been produced, etc) these are the functions we would do it in.

CvPlayerAI.cpp

int CvPlayerAI::AI_getAlignmentAttitude(PlayerTypes ePlayer)
{
int iAttitude;

iAttitude = 0;

if (GC.getLeaderHeadInfo(getPersonalityType()).getAli gnment() == 1)
{
if (GC.getLeaderHeadInfo(GET_PLAYER(ePlayer).getPerso nalityType()).getAlignment() == 1)
{
iAttitude = 2;
}
if (GC.getLeaderHeadInfo(GET_PLAYER(ePlayer).getPerso nalityType()).getAlignment() == 0)
{
iAttitude = 0;
}
if (GC.getLeaderHeadInfo(GET_PLAYER(ePlayer).getPerso nalityType()).getAlignment() == -1)
{
iAttitude = -6;
}
}
if (GC.getLeaderHeadInfo(getPersonalityType()).getAli gnment() == 0)
{
if (GC.getLeaderHeadInfo(GET_PLAYER(ePlayer).getPerso nalityType()).getAlignment() == 1)
{
iAttitude = -2;
}
if (GC.getLeaderHeadInfo(GET_PLAYER(ePlayer).getPerso nalityType()).getAlignment() == 0)
{
iAttitude = 0;
}
if (GC.getLeaderHeadInfo(GET_PLAYER(ePlayer).getPerso nalityType()).getAlignment() == -1)
{
iAttitude = -2;
}
}
if (GC.getLeaderHeadInfo(getPersonalityType()).getAli gnment() == -1)
{
if (GC.getLeaderHeadInfo(GET_PLAYER(ePlayer).getPerso nalityType()).getAlignment() == 1)
{
iAttitude = -4;
}
if (GC.getLeaderHeadInfo(GET_PLAYER(ePlayer).getPerso nalityType()).getAlignment() == 0)
{
iAttitude = -2;
}
if (GC.getLeaderHeadInfo(GET_PLAYER(ePlayer).getPerso nalityType()).getAlignment() == -1)
{
iAttitude = 2;
}
}

return iAttitude;
}

int CvPlayerAI::AI_getUseDeathAttitude(PlayerTypes ePlayer)
{
int iAttitude;

iAttitude = 0;

if (GET_PLAYER(ePlayer).getCapitalCity() != NULL)
{
if (GET_PLAYER(ePlayer).getCapitalCity()->hasBonus((BonusTypes)GC.getInfoTypeForString("BONUS_MANA_DEATH")))
{
iAttitude += GC.getLeaderHeadInfo(getPersonalityType()).getUseD eathAttitudeChange();
}
}

return iAttitude;
}

int CvPlayerAI::AI_getUseEntropyAttitude(PlayerTypes ePlayer)
{
int iAttitude;

iAttitude = 0;

if (GET_PLAYER(ePlayer).getCapitalCity() != NULL)
{
if (GET_PLAYER(ePlayer).getCapitalCity()->hasBonus((BonusTypes)GC.getInfoTypeForString("BONUS_MANA_ENTROPY")))
{
iAttitude += GC.getLeaderHeadInfo(getPersonalityType()).getUseE ntropyAttitudeChange();
}
}

return iAttitude;
}

int CvPlayerAI::AI_getHighCompassionAttitude(PlayerTyp es ePlayer)
{
int iAttitude;

iAttitude = 0;

if (GET_PLAYER(ePlayer).getCivics((CivicOptionTypes)G C.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()).getHigh CompassionAttitudeChange();
}

return iAttitude;
}

int CvPlayerAI::AI_getLowCompassionAttitude(PlayerType s ePlayer)
{
int iAttitude;

iAttitude = 0;

if (GET_PLAYER(ePlayer).getCivics((CivicOptionTypes)G C.getInfoTypeForString("CIVICOPTION_COMPASSION")) == (CivicOptionTypes)GC.getInfoTypeForString("CIVIC_FEND_FOR_THEMSELVES") ||
GET_PLAYER(ePlayer).getCivics((CivicOptionTypes)GC .getInfoTypeForString("CIVICOPTION_COMPASSION")) == (CivicOptionTypes)GC.getInfoTypeForString("CIVIC_SACRIFICE_THE_WEAK"))
{
iAttitude += GC.getLeaderHeadInfo(getPersonalityType()).getLowC ompassionAttitudeChange();
}

return iAttitude;
}

Then we have to adjust the function that calculates the attitude to ask for the attitude modifier from these functions. This is done in the CvPlayerAI::AI_getAttitudeVal(PlayerTypes ePlayer) function.

iAttitude += AI_getSameReligionAttitude(ePlayer);
iAttitude += AI_getDifferentReligionAttitude(ePlayer);
iAttitude += AI_getAlignmentAttitude(ePlayer);
iAttitude += AI_getUseDeathAttitude(ePlayer);
iAttitude += AI_getUseEntropyAttitude(ePlayer);
iAttitude += AI_getHighCompassionAttitude(ePlayer);
iAttitude += AI_getLowCompassionAttitude(ePlayer);
iAttitude += AI_getBonusTradeAttitude(ePlayer);

Finally we have to define the 4 new function we created in the CvPlayerAI.h file.

CvPlayerAI.h

int AI_getSameReligionAttitude(PlayerTypes ePlayer);
int AI_getDifferentReligionAttitude(PlayerTypes ePlayer);
int AI_getAlignmentAttitude(PlayerTypes ePlayer);
int AI_getUseDeathAttitude(PlayerTypes ePlayer);
int AI_getUseEntropyAttitude(PlayerTypes ePlayer);
int AI_getHighCompassionAttitude(PlayerTypes ePlayer);
int AI_getLowCompassionAttitude(PlayerTypes ePlayer);
int AI_getBonusTradeAttitude(PlayerTypes ePlayer);


Thats it. It does seem like a lot of work but now that we have the attributes defined we can adjust them for each leader in the xml, which makes life easy. Also now that we have done the hard part of allowing the game engine to read the attributes from XML and associated them to the leader instances we can easily call them in any new functions we build.

Step 4 (optional): Making your adjustments show up on the diplo screen:

Thanks to Chalid for showing me where to do this. CvGameTextMgr.cpp controls the game text. Making the following addition to CvGameTextMgr::getAttitudeString will allow your attitude adjustments on the diplomacy screen.

CvGameTextMgr.cpp

iAttitudeChange = GET_PLAYER(ePlayer).AI_getAlignmentAttitude(eTarge tPlayer);
if ((iPass == 0) ? (iAttitudeChange > 0) : (iAttitudeChange < 0))
{
if (GC.getLeaderHeadInfo(GET_PLAYER(eTargetPlayer).ge tPersonalityType()).getAlignment() == 1)
{
szTempBuffer.Format(SETCOLR L"%s" ENDCOLR, TEXT_COLOR((iAttitudeChange > 0) ? "COLOR_POSITIVE_TEXT" : "COLOR_NEGATIVE_TEXT"), gDLL->getText("TXT_KEY_MISC_ATTITUDE_GOOD_ALIGNMENT", iAttitudeChange).GetCString());
szBuffer += NEWLINE + szTempBuffer;
}
if (GC.getLeaderHeadInfo(GET_PLAYER(eTargetPlayer).ge tPersonalityType()).getAlignment() == 0)
{
szTempBuffer.Format(SETCOLR L"%s" ENDCOLR, TEXT_COLOR((iAttitudeChange > 0) ? "COLOR_POSITIVE_TEXT" : "COLOR_NEGATIVE_TEXT"), gDLL->getText("TXT_KEY_MISC_ATTITUDE_NEUTRAL_ALIGNMENT", iAttitudeChange).GetCString());
szBuffer += NEWLINE + szTempBuffer;
}
if (GC.getLeaderHeadInfo(GET_PLAYER(eTargetPlayer).ge tPersonalityType()).getAlignment() == -1)
{
szTempBuffer.Format(SETCOLR L"%s" ENDCOLR, TEXT_COLOR((iAttitudeChange > 0) ? "COLOR_POSITIVE_TEXT" : "COLOR_NEGATIVE_TEXT"), gDLL->getText("TXT_KEY_MISC_ATTITUDE_EVIL_ALIGNMENT", iAttitudeChange).GetCString());
szBuffer += NEWLINE + szTempBuffer;
}
} iAttitudeChange = GET_PLAYER(ePlayer).AI_getUseDeathAttitude(eTarget Player);
if ((iPass == 0) ? (iAttitudeChange > 0) : (iAttitudeChange < 0))
{
szTempBuffer.Format(SETCOLR L"%s" ENDCOLR, TEXT_COLOR((iAttitudeChange > 0) ? "COLOR_POSITIVE_TEXT" : "COLOR_NEGATIVE_TEXT"), gDLL->getText("TXT_KEY_MISC_ATTITUDE_DEATH_USE", iAttitudeChange).GetCString());
szBuffer += NEWLINE + szTempBuffer;
}
iAttitudeChange = GET_PLAYER(ePlayer).AI_getUseEntropyAttitude(eTarg etPlayer);
if ((iPass == 0) ? (iAttitudeChange > 0) : (iAttitudeChange < 0))
{
szTempBuffer.Format(SETCOLR L"%s" ENDCOLR, TEXT_COLOR((iAttitudeChange > 0) ? "COLOR_POSITIVE_TEXT" : "COLOR_NEGATIVE_TEXT"), gDLL->getText("TXT_KEY_MISC_ATTITUDE_ENTROPY_USE", iAttitudeChange).GetCString());
szBuffer += NEWLINE + szTempBuffer;
}
iAttitudeChange = GET_PLAYER(ePlayer).AI_getHighCompassionAttitude(e TargetPlayer);
if ((iPass == 0) ? (iAttitudeChange > 0) : (iAttitudeChange < 0))
{
szTempBuffer.Format(SETCOLR L"%s" ENDCOLR, TEXT_COLOR((iAttitudeChange > 0) ? "COLOR_POSITIVE_TEXT" : "COLOR_NEGATIVE_TEXT"), gDLL->getText("TXT_KEY_MISC_ATTITUDE_HIGH_COMPASSION", iAttitudeChange).GetCString());
szBuffer += NEWLINE + szTempBuffer;
}
iAttitudeChange = GET_PLAYER(ePlayer).AI_getLowCompassionAttitude(eT argetPlayer);
if ((iPass == 0) ? (iAttitudeChange > 0) : (iAttitudeChange < 0))
{
szTempBuffer.Format(SETCOLR L"%s" ENDCOLR, TEXT_COLOR((iAttitudeChange > 0) ? "COLOR_POSITIVE_TEXT" : "COLOR_NEGATIVE_TEXT"), gDLL->getText("TXT_KEY_MISC_ATTITUDE_LOW_COMPASSION", iAttitudeChange).GetCString());
szBuffer += NEWLINE + szTempBuffer;
}

And of course the text strings I used in the above code need to be put in one of the files in your ‘/Assets/xml/text/’ directory. The following are the defines I use but you can translate them to whatever fits your mod:

CIV4GameText_FFH2.xml (or any file in the ‘/Assets/xml/text’ directory)

<TEXT>
<Tag>TXT_KEY_MISC_ATTITUDE_EVIL_ALIGNMENT</Tag>
<English>%D1: "You are Evil."</English>
<French>%D1: "You are Evil."</French>
<German>%D1: "You are Evil."</German>
<Italian>%D1: "You are Evil."</Italian>
<Spanish>%D1: "You are Evil."</Spanish>
</TEXT>
<TEXT>
<Tag>TXT_KEY_MISC_ATTITUDE_GOOD_ALIGNMENT</Tag>
<English>%D1: "You are Good."</English>
<French>%D1: "You are Good."</French>
<German>%D1: "You are Good."</German>
<Italian>%D1: "You are Good."</Italian>
<Spanish>%D1: "You are Good."</Spanish>
</TEXT>
<TEXT>
<Tag>TXT_KEY_MISC_ATTITUDE_NEUTRAL_ALIGNMENT</Tag>
<English>%D1: "You are Neutral."</English>
<French>%D1: "You are Neutral."</French>
<German>%D1: "You are Neutral."</German>
<Italian>%D1: "You are Neutral."</Italian>
<Spanish>%D1: "You are Neutral."</Spanish>
</TEXT>
<TEXT>
<Tag>TXT_KEY_MISC_ATTITUDE_DEATH_USE</Tag>
<English>%D1: "You use Death magic."</English>
<French>%D1: "You use Death magic."</French>
<German>%D1: "You use Death magic."</German>
<Italian>%D1: "You use Death magic."</Italian>
<Spanish>%D1: "You use Death magic."</Spanish>
</TEXT>
<TEXT>
<Tag>TXT_KEY_MISC_ATTITUDE_ENTROPY_USE</Tag>
<English>%D1: "You use Entropy magic."</English>
<French>%D1: "You use Entropy magic."</French>
<German>%D1: "You use Entropy magic."</German>
<Italian>%D1: "You use Entropy magic."</Italian>
<Spanish>%D1: "You use Entropy magic."</Spanish>
</TEXT>
<TEXT>
<Tag>TXT_KEY_MISC_ATTITUDE_HIGH_COMPASSION</Tag>
<English>%D1: "You treat your people well."</English>
<French>%D1: "You treat your people well."</French>
<German>%D1: "You treat your people well."</German>
<Italian>%D1: "You treat your people well."</Italian>
<Spanish>%D1: "You treat your people well."</Spanish>
</TEXT>
<TEXT>
<Tag>TXT_KEY_MISC_ATTITUDE_LOW_COMPASSION</Tag>
<English>%D1: "You treat your people poorly."</English>
<French>%D1: "You treat your people poorly."</French>
<German>%D1: "You treat your people poorly."</German>
<Italian>%D1: "You treat your people poorly."</Italian>
<Spanish>%D1: "You treat your people poorly."</Spanish>
</TEXT>

TheLopez
Apr 13, 2006, 11:02 AM
Very cool Kael, thanks!!!

Kael
Apr 13, 2006, 11:05 AM
Very cool Kael, thanks!!!

Yeah, I want to use this to be able to set variables on leaders for their preference for hiring/selling mercenaries.

woodelf
Apr 13, 2006, 11:08 AM
Fantastic stuff Kael. A good guide.

Thasis
Apr 13, 2006, 01:05 PM
Dude! Awsome tutorial! I'm definitly going to need this for my mod.

The Great Apple
Apr 13, 2006, 03:44 PM
Thanks for this - will save me quite a while trying to work it out myself!

Grey Fox
Apr 13, 2006, 09:02 PM
Great walkthrough. Will make life easier for me. ^_^

Aussie_Lurker
Apr 13, 2006, 11:50 PM
It looked really easy right up to the last step ;). I am curious, have you created a 'modification only' version of the game so that you can play around with all the .dlls without fear of ruining your actual game? Or is the system more robust than that? Just wondering before I take the plunge myself.

Aussie_Lurker.

Dianthus
Apr 14, 2006, 04:11 AM
The SDK is only one dll (CvGameCoreDLL.dll), and you don't need to overwrite the original to use it. Just stick it under "My Games\Sid Meier's Civilization 4\CustomAssets" to use it for a normal game, or it can go inside a mod. I.e. In "My Games\Sid Meier's Civilization 4\MODS\<modname>\Assets".

Impaler[WrG]
Apr 15, 2006, 10:15 PM
This is a very usefull tutorial (which I could never have figured out on my own) I would like to point out a what I think would have been a better way to implement the same effect.

Rather then create XML tags for specific Bonuses a generalized <BonusDiplomacyModifers> grouping would hold any number of individual Modifiers, each modifier would speficy a Resorce an Int and a Boolean.

The C code would then be something like this psudocode

if ( not (OtherPlayer.hasResorce("InputResorce") xor inputBoolean) )
DiplomaticStatus += InputInt

The effect is that the Boolean controls if the diplomatic modifier is aplied when they have it (true) or dont have it (false). So if you wanted to have an empire hate other empires that use Death Magic (or any other resorce) you XML would be ("Death_Magic", -3, true). Or you could have them love anyone who isn't using Entropy ("Entropy_Magic", +2, false). This should be much more flexible system and it can be used by other mod makers in the future.

talchas
Apr 16, 2006, 08:40 AM
One last bit of housekeeping before we start the real programming. We need to assign the new attributes to the LeaderHead object so that they can be queried for specific instances of leaders. This is done in the CyInfoInterface3.cpp file.
Not quite true - CyInfoInterface3.cpp (and any Cy*Interface#.cpp file) exposes stuff to python, so you only need to worry about it if you want what you added exposed to python.

Kael
Apr 16, 2006, 08:44 AM
Not quite true - CyInfoInterface3.cpp (and any Cy*Interface#.cpp file) exposes stuff to python, so you only need to worry about it if you want what you added exposed to python.

Good to know, I'll update the writeup.

Kael
Apr 22, 2006, 09:49 AM
I updated the first post to add another attitude modifer, alignment. Depending on the alignment of the target civlization and the deciding civilization a different attitude modifier is applied.

I also added a section that allows your modifiers to display on the diplomacy screen (thanks to Chalid for pointing out where this is done) and a screen shot of the finished result in action.

Alignment is especially interesting as you could use it in your mods to represent any sort of informal relationship between civs. If, for example, you wanted certain civs to get along well and others not to.

Enjoy!

Aussie_Lurker
Apr 29, 2006, 09:56 PM
Hiya Kael.

First up, I have to say that I THINK I am beginning to understand this, but I do have a couple of questions:

1) Can this be used to add ANY new XML attribute? For instance, I want certain civics options to be able to increase Specialist Yields (Hammers, Food, Gold) and not simply commerces (culture, research and gold). Could I do this using the SDK? I know it can technically be done in Python, but I don't know how to then make players aware of the effects.

2) The file in question consists of a HUGE number of .cpp files, and I confess that it is somewhat bewildering!!! Can you point me towards a comprehensive list of the .cpp's, and what they primarily effect?

Thanks in advance.

Aussie_Lurker.

Merum
Apr 29, 2006, 11:21 PM
This is awesome, and right in time for me to use it in my own mod! Thanks, dude!

Kael
May 02, 2006, 06:24 AM
Hiya Kael.

First up, I have to say that I THINK I am beginning to understand this, but I do have a couple of questions:

1) Can this be used to add ANY new XML attribute? For instance, I want certain civics options to be able to increase Specialist Yields (Hammers, Food, Gold) and not simply commerces (culture, research and gold). Could I do this using the SDK? I know it can technically be done in Python, but I don't know how to then make players aware of the effects.

Yes, and adding the attributes is pretty easy. Getting the attributes to do something is the hard part. Adding schema attributes and modifing the CvCity.cpp::doTurn() function and the text output (probably somewhere around CvGameTextMgr.cpp::parseCivicInfo() ) is the "best" way to do what you are describing. The simpliest like you say would be to modify python and just update the strategy tag to tell the player what it is doing.

2) The file in question consists of a HUGE number of .cpp files, and I confess that it is somewhat bewildering!!! Can you point me towards a comprehensive list of the .cpp's, and what they primarily effect?

Thanks in advance.

Aussie_Lurker.

Nope, I don't have any list. The names are pretty descriptive. The Cv... files are the real files. The Cy... version are for export to Python. The .cpp files are the code and the .h files are the headers which define the variables. I spend a lot of time in CvUnit.cpp, CvCity.cpp, CvGame.cpp, CvPlayer.cpp and CvPlot.cpp.

Aussie_Lurker
May 07, 2006, 06:26 AM
Kael, first of all, I want to thank you for ALL of your assistance up until this point. I wouldn't even be able to ask you this question without your help.

Now, as you know, I am a bit of a dunce when it comes to Python :mischief:, but I feel your tutorial above has given me the best chance yet of avoiding the need to do Python in many cases-simply by adding new XML 'hooks' via the SDK.
Now I have followed all of your instruction to this point, and feel fairly confident that they should work, and am now up to your 'optional' section.
What I have basically done is added a getStateReligionCommerceModifier and getStateReligionYieldModifier for civics options.
I have added them to both the cvInfos file-in both places-as well as in the cvInfos.h file.
The question is, do I REALLY need to do anything more at this point or, as I believe, can I go ahead and compile? If you can let me know, I would VERY much appreciate it :).

Aussie_Lurker.

Kael
May 08, 2006, 02:43 AM
Kael, first of all, I want to thank you for ALL of your assistance up until this point. I wouldn't even be able to ask you this question without your help.

Now, as you know, I am a bit of a dunce when it comes to Python :mischief:, but I feel your tutorial above has given me the best chance yet of avoiding the need to do Python in many cases-simply by adding new XML 'hooks' via the SDK.
Now I have followed all of your instruction to this point, and feel fairly confident that they should work, and am now up to your 'optional' section.
What I have basically done is added a getStateReligionCommerceModifier and getStateReligionYieldModifier for civics options.
I have added them to both the cvInfos file-in both places-as well as in the cvInfos.h file.
The question is, do I REALLY need to do anything more at this point or, as I believe, can I go ahead and compile? If you can let me know, I would VERY much appreciate it :).

Aussie_Lurker.

Assuming they are being called by whereever the turn upkeeps are generated it sounds like you've got it.

Aussie_Lurker
May 08, 2006, 10:04 PM
Kael, I am afraid my first attempt failed, but I feel certain this can be rectified. You mentioned that as long as the functions are being called-but how do I make certain of this before I go ahead and compile?

Aussie_Lurker.

Kael
May 09, 2006, 03:17 AM
Kael, I am afraid my first attempt failed, but I feel certain this can be rectified. You mentioned that as long as the functions are being called-but how do I make certain of this before I go ahead and compile?

Aussie_Lurker.

By "called" I just mean that there is some functon that is using your new attributes. I would highly recommend compiling as you go and making sure the dll works. There is nothing worse than doing a ton of work, then compiling and figuring out it doesn't work. I recommended a few compiling points in the first post.

Aussie_Lurker
May 09, 2006, 07:42 AM
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.

Raumohir
Jun 05, 2006, 11:37 AM
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.

Gerikes
Jun 06, 2006, 11:59 AM
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:


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:


gc.getCivilizationInfo(iNum).getNumLeaders()


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


gc.getCivilizationInfo(iNum).yourFunctionHere()


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

Edit: This thread (http://forums.civfanatics.com/showthread.php?t=167476) has a pretty good writeup for that too.

Lord Olleus
Jun 22, 2006, 01:22 AM
How do you add multiple lines tag in XML. By that I mean something like:

<UnitClassUpgrades>
<UnitClassUpgrade>
<UnitClassUpgradeType>UNITCLASS_KNIGHT</UnitClassUpgradeType>
<bUnitClassUpgrade>1</bUnitClassUpgrade>
</UnitClassUpgrade>
</UnitClassUpgrades>

zulu9812
Jun 22, 2006, 06:28 AM
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 (http://forums.civfanatics.com/showthread.php?t=174712)). 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

int CvPlayerAI::AI_getHighCompassionAttitude(PlayerTyp es ePlayer)
{
int iAttitude;

iAttitude = 0;

if (GET_PLAYER(ePlayer).getCivics((CivicOptionTypes)G C.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()).getHigh CompassionAttitudeChange();
}

return iAttitude;
}

which I changed to:

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

iAttitude = 0;

if (GET_PLAYER(ePlayer).getCivics((CivicOptionTypes)G C.getInfoTypeForString("CIVICOPTION_WARFARE")) == (CivicOptionTypes)GC.getInfoTypeForString("CIVIC_PEACEKEEPING") ||
GET_PLAYER(ePlayer).getCivics((CivicOptionTypes)GC .getInfoTypeForString("CIVICOPTION_WARFARE")) == (CivicOptionTypes)GC.getInfoTypeForString("CIVIC_PEACKEEPING"))
{
iAttitude += GC.getLeaderHeadInfo(getPersonalityType()).getHigh CompassionAttitudeChange();
}

return iAttitude;
}

Any thoughts?

Kael
Jun 22, 2006, 08:58 AM
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 (http://forums.civfanatics.com/showthread.php?t=174712)). 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:

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

iAttitude = 0;

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

return iAttitude;
}

But I dont think that is your problem. Can you attach your CvPlayerAI.cpp file?

Impaler[WrG]
Jun 22, 2006, 10:14 AM
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.

Gerikes
Jun 23, 2006, 04:33 PM
How do you add multiple lines tag in XML. By that I mean something like:

<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:


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".

Impaler[WrG]
Jun 23, 2006, 10:58 PM
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.

Kael
Jun 23, 2006, 11:15 PM
']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.

zulu9812
Jun 26, 2006, 02:52 PM
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

Kael
Jun 26, 2006, 03:07 PM
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?

zulu9812
Jun 26, 2006, 04:18 PM
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.

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"

Kael
Jun 26, 2006, 04:34 PM
I woudl still recommend cleaning up your AI_getPeacekeepingAttitude as follows:


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

iAttitude = 0;

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

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).

zulu9812
Jun 26, 2006, 06:49 PM
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

Kael
Jun 26, 2006, 08:27 PM
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?

zulu9812
Jun 27, 2006, 01:54 AM
Oh, does the other civ have it as his favorite civic?

No, I haven't touched that yet.

zulu9812
Jun 29, 2006, 03:09 AM
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,

Impaler[WrG]
Jun 29, 2006, 03:36 AM
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.

zulu9812
Jun 29, 2006, 12:18 PM
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

The Great Apple
Jul 02, 2006, 04:52 PM
Has anybody mananged to find a way to add/edit classInfos, such as Civ4UnitClassInfo.xml? Taking that file as an example, it would appear they are defined in CvGlobals.cpp with referances to data which doesn't seem to be set anywhere else?

EDIT: NVM, while I still can't work out how to edit them, I have worked out how to add new ones using the same method as adding normal XML files.

The Great Apple
Jul 11, 2006, 04:48 PM
Feel free to check out FfH2, we have a new xml document 'CIV4SpellInfos.xml' that we use and it works great.I just downloaded FfH2 just so I could check out the source code required - I can't quite get the XML to be read properly... however, there seems to be a source-code shortage. Any chance I could take a look at it?

Impaler[WrG]
Jul 12, 2006, 12:30 PM
Zulu9812:

I think the problem is in your get PeacekeepingAtttiude function, specificaly the if statment that controls it

if (GET_PLAYER(ePlayer).getCivics((CivicOptionTypes)G C.getInfoTypeForString("CIVICOPTION_WARFARE")) == (CivicOptionTypes)GC.getInfoTypeForString("CIVIC_PEACEKEEPING") ||
GET_PLAYER(ePlayer).getCivics((CivicOptionTypes)GC .getInfoTypeForString("CIVICOPTION_WARFARE")) == (CivicOptionTypes)GC.getInfoTypeForString("CIVIC_PEACEKEEPING"))
{
iAttitude += GC.getLeaderHeadInfo(getPersonalityType()).getPeac ekeepingAttitudeChange();
}

First off you have 2 identical comparisons being OR'ed together which is unessary. Second the static cast of (CivicOptionTypes) in the later half of the comparison should be a cast to (CivicOption), arn't you getting a compiling error here for incompatible comparison as the first half of the comparison's getCivics is going to return a CivicOption enum??

Though I still cant say ware the +1 is coming from, everything else looks ok to me.

Slynky
Jul 12, 2006, 06:36 PM
I'm not sure if this is the right place to ask but have any of you looked into how specific unit bonuses work?
I've been trying to expand the number of promotions. Unfortunatly there are no tags for some of the variables that I'd like to modify such as "chance to intercept" or "traverse impassable terrain"
This is the only thread i've been able to find on creating new XML Tags so if anyone could halp me adapt this to the promotioninfos.xml I'd really appreciate it.

Gerikes
Jul 12, 2006, 07:05 PM
I'm not sure if this is the right place to ask but have any of you looked into how specific unit bonuses work?
I've been trying to expand the number of promotions. Unfortunatly there are no tags for some of the variables that I'd like to modify such as "chance to intercept" or "traverse impassable terrain"
This is the only thread i've been able to find on creating new XML Tags so if anyone could halp me adapt this to the promotioninfos.xml I'd really appreciate it.

Sure. First off, realize that in order to add attributes you'll need to have the ability to modify and recompile the DLL, which is written in c++. Check out this thread (http://forums.civfanatics.com/showthread.php?t=166933) for more on compiling the DLL. If you don't know c++, but have some programming background (perhaps in Java, C, or C#), you might be able to sqeak through, but if you run into a problem you're probably going to have a long night (or two) ahead of you.

So, I would try just getting the SDK code to compile first, without even changing any of it. If you can get that far, and feel comfortable with it, or are almost there and need a bit of help, check back in, and I'll see what I can do.

Lord Olleus
Oct 08, 2006, 03:28 PM
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:


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".

I am having trouble doing this. The code is:

UnitInfos.xml


<FreeTechPromotions>
<FreeTechPromotion>
<PrereqTech>TECH_THE_WHEEL</PrereqTech>
<PromotionType>PROMOTION_SHOCK</PromotionType>
</FreeTechPromotion>
</FreeTechPromotions>



Infos.cpp

...

int CvUnitInfo::getFreeTechPromotions(int i) const
{
FAssertMsg(i < GC.getNumTechInfos(), "Index out of bounds");
FAssertMsg(i > -1, "Index out of bounds");
return m_piFreeTechPromotions ? m_piFreeTechPromotions[i] : -1;
}

...
...

SAFE_DELETE_ARRAY(m_piFreeTechPromotions);
m_piFreeTechPromotions = new int[GC.getNumTechInfos()];
stream->Read(GC.getNumTechInfos(), m_piFreeTechPromotions);

...
...

pXML->SetVariableListTagPair(&m_piFreeTechPromotions, "FreeTechPromotions", GC.getTechInfo(), sizeof(GC.getTechInfo((TechTypes)0)), GC.getNumTechInfos(), -1);

...

Didn't include 1 liners as even I can't get them wrong.


Basicaly what I am trying to do is give a certain promotion to a unit type when a technology is discovered. The mod loads fine, but when I print the out putgetFreeTechPromotions, I get -1 one from every tech, as should be, apart from THE_WHEEL where I get 1627180, instead of 6. If I quit the game and restart it, I still get the same number. If I change the tech or the promotiton I get a different 7 digit number, but it still repeats if I reload the game. If I put a number in the <promotiontype> tag I get the same 7 digit number, no matter which number I put in.

This is driving mad, does anyone know whats causing this?

talchas
Oct 08, 2006, 06:18 PM
The problem is that SetVariableListTagPair in this case expects the second element of the XML to be an integer, because you passed it an integer array. It doesn't try to turn a string into a number by looking it up as a Civ4 tech/unit/unitclass/promotion/whatever name. The best way would probably be to use the string version like something like this:

CvString[] temp = new CvString[GC.getNumTechInfos()];
m_piFreeTechPromotions = new int[GC.getNumTechInfos()]
pXML->SetVariableListTagPair(&temp, "FreeTechPromotions", GC.getTechInfo(), sizeof(GC.getTechInfo((TechTypes)0)), GC.getNumTechInfos(), "");
for(int i = 0; i< GC.getNumTechInfos(); i++){
if(temp[i].IsEmpty())
m_piFreeTechPromotions[i] = -1;
else
m_piFreeTechPromotions[i] =CvXMLLoadUtility::FindInInfoClass( temp[i], GC.getTechInfo(), sizeof(GC.getTechInfo((TechTypes)0)), GC.getNumTechInfos());
}
delete[] temp;

(note: I haven't tested this, and I'm not sure if the delete[] temp is the proper deletion method) The rest would stay the same, only the Read(xmlthingy) method would change.

Lord Olleus
Oct 09, 2006, 06:05 AM
THanks for your help, I've got it working, there were just a few things that needed ironing out. You typed in CvString[] instead of CvString* on the first line, and for ::FindInInfoClass() required promotioninfo stuff and not techinfo. I think that delete[] is that right command to delete arrays. This is the final code I am using:

CvString* temp = new CvString[GC.getNumTechInfos()];
m_piFreeTechPromotions = new int[GC.getNumTechInfos()];

pXML->SetVariableListTagPair(&temp, "FreeTechPromotions", GC.getTechInfo(), sizeof(GC.getTechInfo((TechTypes)0)), GC.getNumTechInfos(), "");
for(int i = 0; i< GC.getNumTechInfos(); i++)
{
if(temp[i].IsEmpty())
{
m_piFreeTechPromotions[i] = -1;
}
else
{
m_piFreeTechPromotions[i] = CvXMLLoadUtility::FindInInfoClass( temp[i], GC.getPromotionInfo(), sizeof(GC.getPromotionInfo((PromotionTypes)0)), GC.getNumPromotionInfos());
}
}
//Must let go of pointer
delete[] temp;


I was thinking about creating a new function which did this in case other people also wanted to do something similar.
Thanks again, this will be very usefull.

Civkid1991
Oct 10, 2006, 07:17 AM
wow keal stricks again... cool tutorial :goodjob:

btw.. could the same idea be used to modify things like culture, or production, etc (edit xml, add to sdk)?

Kael
Oct 10, 2006, 07:53 PM
wow keal stricks again... cool tutorial :goodjob:

btw.. could the same idea be used to modify things like culture, or production, etc (edit xml, add to sdk)?

Yes, the process is definitly applicable to more than just this one example.

gordonthewhale
Nov 03, 2006, 10:02 AM
Great tutorial! I'm trying to add some tags to Civ4ImprovementInfos so that improvements can require bonuses to construct, in the same way that railroads require coal. I got a little greedy, and wanted to make multiple bonuses possible, and multiple ways of checking their presence. I added my tags inside the BonusTypeStruct of CivTerrainInfos.xml, as follows:


<ElementType name="BonusType" content="textOnly"/>
<ElementType name="bBonusMakesValid" content="textOnly" dt:type="boolean"/>
<!--begin adds -->

<!-- The improvement requires that the bonus be available at the actual plot (ie must be on a trade route) -->
<ElementType name="bBonusRequiredAt" content="textOnly" dt:type="boolean"/>
<!-- The improvement requires the bonus be available at an adjacent plot (as for railroads/coal)-->
<ElementType name="bBonusRequiredAdjacent" content="textOnly" dt:type="boolean"/>
<!-- The improvement requires the bonus be available at the nearest city-->
<ElementType name="bBonusRequiredCity" content="textOnly" dt:type="boolean"/>
<!-- The improvement requires the bonus be available in the Capital -->
<ElementType name="bBonusRequiredCapital" content="textOnly" dt:type="boolean"/>
<!-- The improvement requires the resource be somewhere in the civilization -->
<ElementType name="bBonusRequiredAnywhere" content="textOnly" dt:type="boolean"/>

<!-- end adds -->

<ElementType name="bBonusTrade" content="textOnly" dt:type="boolean"/>
<ElementType name="iDiscoverRand" content="textOnly" dt:type="int"/>
<ElementType name="BonusTypeStruct" content="eltOnly">
<element type="BonusType"/>
<element type="bBonusMakesValid"/>
<!-- begin adds -->
<element type="bBonusRequiredAt"/>
<element type="bBonusRequiredAdjacent"/>
<element type="bBonusRequiredCity"/>
<element type="bBonusRequiredCapital"/>
<element type="bBonusRequiredAnywhere"/>
<!-- end adds -->
<element type="bBonusTrade"/>
<element type="iDiscoverRand"/>
<element type="YieldChanges" minOccurs="0" maxOccurs="*"/>
</ElementType>

I then added the new data to CivImprovementInfos.xml. this loaded okay, though after looking at the C++, I see that the data wasn't properly loaded without some changes.

I followed all the rest of the steps, with some changes to the XML read step, because the BonusTypeStruct is read differently (CvXMLLoadUtilitySet.cpp):
void CvXMLLoadUtility::SetImprovementBonuses(CvImprovem entBonusInfo** ppImprovementBonus)
{
....
....

// initialize the boolean list to the correct size and all the booleans to false
InitImprovementBonusList(ppImprovementBonus, GC.getNumBonusInfos());
// set the local pointer to the memory we just allocated
paImprovementBonus = *ppImprovementBonus;

...
...

GetNextXmlVal(&paImprovementBonus[iBonusIndex].m_bBonusMakesValid);

/* additions */
GetNextXmlVal(&paImprovementBonus[iBonusIndex].m_bBonusRequiredAt);
GetNextXmlVal(&paImprovementBonus[iBonusIndex].m_bBonusRequiredAdjacent);
GetNextXmlVal(&paImprovementBonus[iBonusIndex].m_bBonusRequiredCity);
GetNextXmlVal(&paImprovementBonus[iBonusIndex].m_bBonusRequiredCapital);
GetNextXmlVal(&paImprovementBonus[iBonusIndex].m_bBonusRequiredAnywhere);
/* end additions*/

GetNextXmlVal(&paImprovementBonus[iBonusIndex].m_bBonusTrade);
GetNextXmlVal(&paImprovementBonus[iBonusIndex].m_iDiscoverRand);

...
...
}

I also added some lines to initImprovementBonusList, to initialize the new members to false.

It still parses the XML fine, but the program is crashing sometime in the first few turns. I did add some implementation code, but it's all commented out now. I'm guessing that bad memory is being allocated somewhere, but in a codebase this big, I can't begin to figure out where. Any ideas?

Edit: Figured it out, I had null pointers cropping up in certain cases (trying to get the barbarian's capital, for instance).

Duke176
Apr 11, 2007, 10:59 AM
I would make you a question about adding XML new tags.
Why, and how to avoid, I add a new tag to a Building (putting minOccurs="0"/) so I don't have to add it everywhere, I make all the steps required by Tutorial, but when the game load, it crushes while loading XML (it says it's a prb of ci4SpecialBuildings... anyway I know it's a prb of new xml tag I added.
Once I added to units and to improvements new tags, for units no problems, and for improvements same problem.

so anyone knows why?

I link here 2 threads to show you more pricesely what happened with improvements that is the same as buildigns.
thx.

http://forums.civfanatics.com/showthread.php?t=216260
http://forums.civfanatics.com/showthread.php?t=195251

PLS, I don't know how i solved that prb, and I would understand how to avoid to make it happens again.

Kael
Apr 11, 2007, 01:55 PM
Attach the relevant files and I will take a look (modified building file, schema file, and any changed SDK files).

Duke176
Apr 11, 2007, 05:03 PM
thx Kael you are always the best.
I copy here the link for the download, I have already uploaded them in another thread.

Pls only explain me what's wrong so I can avoid to make the same error again.
http://forums.civfanatics.com/attach...7&d=1176082735 - link to the file


I refer to those identified by "OIL CONSUMPTION"

Kael
Apr 11, 2007, 05:39 PM
thx Kael you are always the best.
I copy here the link for the download, I have already uploaded them in another thread.

Pls only explain me what's wrong so I can avoid to make the same error again.
http://forums.civfanatics.com/attach...7&d=1176082735 - link to the file


I refer to those identified by "OIL CONSUMPTION"

File not found.

Duke176
Apr 11, 2007, 07:54 PM
sorry it worked for me 2 mins before...

here you are.

Kael
Apr 12, 2007, 09:51 AM
sorry it worked for me 2 mins before...

here you are.

It looks to be defined pretty well. But if I were you I would add that attribute to all buildings, even if it is just set to 0. Even though you have set the min value to make no occurances valid according to xml schema the SDK still has to read it.

And it should be easy to do a search and replace for </FoodKept> and throw <iOilConsumption>0</iOilConsumption> behind it.

I would also recommend removing comments from the xml files. I have seen some odd problems with those, the SDK isnt the most XML aware app in the world.

Let me know if either of those solve your issue.

Duke176
Apr 12, 2007, 10:01 AM
As soon I'll be at home, I'll do it and let you know.
Thx very much for your work. :)

Duke176
Apr 12, 2007, 01:38 PM
it returns always the same prb...
really I don't figure why...

Kael
Apr 12, 2007, 02:33 PM
it returns always the same prb...
really I don't figure why...

Okay, remove your custom dll and see if you have the issue (ie: see if it will load just the modified schema and xml).

Duke176
Apr 12, 2007, 06:00 PM
doesn't work always the same.

Kael
Apr 13, 2007, 07:47 AM
Weird. K, link up your entire mod and I'll check it out.

Duke176
Apr 13, 2007, 11:49 AM
oki thx.
here you are.

http://forums.civfanatics.com/downloads.php?do=file&id=4593 (hope they don't delete it, I'll do it once we solved prb)

To the Stuff: pls dont' delete that file just for the time needed to Kael to download. The prb I've encountered could be suffered by anyone and if we don't find a way to solve it, it will get harder to dev mods. Thx.

Kael
Apr 14, 2007, 06:30 PM
oki thx.
here you are.

http://forums.civfanatics.com/downloads.php?do=file&id=4593 (hope they don't delete it, I'll do it once we solved prb)

To the Stuff: pls dont' delete that file just for the time needed to Kael to download. The prb I've encountered could be suffered by anyone and if we don't find a way to solve it, it will get harder to dev mods. Thx.

The files you uploaded seemed to be compressed twice. Once into a zip and another time in an "ace" file? Could you upload a copy that is just zipped, I cant uncompress ace files.

Duke176
Apr 15, 2007, 12:20 PM
http://forums.civfanatics.com/downloads.php?do=file&id=4604

here is new link less compressed.

Duke176
Apr 22, 2007, 05:18 AM
any news Kael?

Jean Elcard
Jul 01, 2007, 06:19 PM
Many thx for this tutorial. Lead me to my first own SDK mod. Thx Kael.

Edit 1: After the first immediate success with single value tags, I tried to add something more complicated with child tags. I was sure it should work, but it didn't. Civ kept crashing when loading the XML Files. Then (many hours later and after starting to try to connect mentally with the XML base code of CIV 4) I recognized, the game even crashed, if I only add the member variable's declaration in the header file and the initialisation of this pointer to a bool array in the constructor. Very strange ...

Eventually I decided to compile the whole project again by clearing my "Final Release" folder completely and oh wonder, it worked! So my tipp, if you run in a similar problem and you are tired of trying to compile and debug the source code by starring at it over and over again, try the same drastic meassure. Maybe and only maybe it's going to help.

Mylon
Aug 21, 2007, 11:56 PM
So how does this schema thing work, anyhow? I insert the fields in the schema, and the insert the fields into the regular xml as normal, but then the game says the file is not according to the schema? When I placed the new field at the end of the entry, it loaded fine. Likewise in another instance, I placed the new field near the beginning of the entry in the schema and the game expected it sooner. And, one more time while adding an entry and it seems that it expected the new entry at the end of the file (but in the schema the space for the new entries are near the beginning).

Edit: I found some really weird behavior. While trying to edit Civ4BonusInfos.xml, the game prompted me for an entry "iAIObjective" which no search turns up anything in the XML (in the vanilla folder or the BtS folder) files _except_ in the schema. Anyone know anything about this?

CornPlanter
Aug 25, 2007, 10:11 AM
Step 4 (optional): Making your adjustments show up on the diplo screen:

Thanks to Chalid for showing me where to do this. CvGameTextMgr.cpp controls the game text. Making the following addition to CvGameTextMgr::getAttitudeString will allow your attitude adjustments on the diplomacy screen.

CvGameTextMgr.cpp


I can't get this part working. Or, actually, I don't know which part doesn't work because I can't tell if attitude changes or not without seeing it in diplomacy screens. I added only Alignment thing but I don't know where's the problem: maybe something wrong with reading from xml, maybe something wrong with calculating attitudes, maybe something wrong with displaying it in diplomacy window. Could somebody help me with debuging please? ;) Changes I made (Everything is pretty much as in Kael's examples with some changes. I don't know, maybe the error is pretty obvious, but since it's my first try with SDK...):

CIV4CivilizationsSchema.xml

<ElementType name="ArtDefineTag" content="textOnly"/>
<ElementType name="iAlignment" content="textOnly" dt:type="int"/>
<ElementType name="iWonderConstructRand" content="textOnly" dt:type="int"/>
.....
<ElementType name="LeaderHeadInfo" content="eltOnly">
<element type="Type"/>
<element type="Description"/>
<element type="Civilopedia"/>
<element type="ArtDefineTag"/>
<element type="iAlignment"/>
<element type="iWonderConstructRand"/>


CIV4LeaderHeadInfos.xml

<ArtDefineTag>ART_DEF_LEADER_BRENNUS</ArtDefineTag>
<iAlignment>-20</iAlignment>
<iWonderConstructRand>0</iWonderConstructRand>
(different for each leader; range -100 to 100)


CvInfos.cpp

CvLeaderHeadInfo::CvLeaderHeadInfo() :
m_iAlignment(0),
m_iWonderConstructRand(0),
.......
int CvLeaderHeadInfo::getSameReligionAttitudeChange() const
{
return m_iSameReligionAttitudeChange;
}
int CvLeaderHeadInfo::getAlignment() const
{
return m_iAlignment;
}

void CvLeaderHeadInfo::setAlignment(int i)
{
m_iAlignment = i;
}
int CvLeaderHeadInfo::getSameReligionAttitudeDivisor() const
{
return m_iSameReligionAttitudeDivisor;
}
.......

stream->Read(&m_iDifferentReligionAttitudeChangeLimit);
stream->Read(&m_iAlignment);
stream->Read(&m_iBonusTradeAttitudeDivisor);

.......

stream->Write(m_iDifferentReligionAttitudeChangeLimit);
stream->Write(m_iAlignment);
stream->Write(m_iBonusTradeAttitudeDivisor);

......

pXML->GetChildXmlValByName(&m_iDifferentReligionAttitudeChangeLimit, "iDifferentReligionAttitudeChangeLimit");
pXML->GetChildXmlValByName(&m_iAlignment, "iAlignment");
pXML->GetChildXmlValByName(&m_iBonusTradeAttitudeDivisor, "iBonusTradeAttitudeDivisor");


CvInfos.h

DllExport int getDifferentReligionAttitudeChangeLimit() const;
DllExport int getAlignment() const;
DllExport void setAlignment(int i);
DllExport int getBonusTradeAttitudeDivisor() const;
......
int m_iDifferentReligionAttitudeChangeLimit;
int m_iAlignment;
int m_iBonusTradeAttitudeDivisor;


CvPlayerAI.cpp

int CvPlayerAI::AI_getAlignmentAttitude(PlayerTypes ePlayer)
{
int iAttitude;
iAttitude = 0;

if (GC.getLeaderHeadInfo(getPersonalityType()).getAli gnment() > 0)
{
if (GC.getLeaderHeadInfo(GET_PLAYER(ePlayer).getPerso nalityType()).getAlignment() < 0)
{
iAttitude = -3;
}
if (GC.getLeaderHeadInfo(GET_PLAYER(ePlayer).getPerso nalityType()).getAlignment() == 0)
{
iAttitude = 1;
}
if (GC.getLeaderHeadInfo(GET_PLAYER(ePlayer).getPerso nalityType()).getAlignment() > 0)
{
iAttitude = 4;
}
}

else if (GC.getLeaderHeadInfo(getPersonalityType()).getAli gnment() == 0)
{
if (GC.getLeaderHeadInfo(GET_PLAYER(ePlayer).getPerso nalityType()).getAlignment() < -80)
{
iAttitude = -3;
}
if (GC.getLeaderHeadInfo(GET_PLAYER(ePlayer).getPerso nalityType()).getAlignment() < 80 && GC.getLeaderHeadInfo(GET_PLAYER(ePlayer).getPerson alityType()).getAlignment() > -80)
{
iAttitude = 1;
}
if (GC.getLeaderHeadInfo(GET_PLAYER(ePlayer).getPerso nalityType()).getAlignment() > 80)
{
iAttitude = -2;
}
}
else if (GC.getLeaderHeadInfo(getPersonalityType()).getAli gnment() < 0)
{
if (GC.getLeaderHeadInfo(GET_PLAYER(ePlayer).getPerso nalityType()).getAlignment() < 0)
{
iAttitude = 2;
}
else if (GC.getLeaderHeadInfo(GET_PLAYER(ePlayer).getPerso nalityType()).getAlignment() == 0)
{
iAttitude = -1;
}
else if (GC.getLeaderHeadInfo(GET_PLAYER(ePlayer).getPerso nalityType()).getAlignment() > 0)
{
iAttitude = -3;
}
}

return iAttitude;
}

......

iAttitude += AI_getRivalTradeAttitude(ePlayer);
iAttitude += AI_getAlignmentAttitude(ePlayer);


CvPlayerAI.h

int AI_getDifferentReligionAttitude(PlayerTypes ePlayer);
int AI_getAlignmentAttitude(PlayerTypes ePlayer);
int AI_getBonusTradeAttitude(PlayerTypes ePlayer);


CvGameTextMgr.cpp

iAttitudeChange = GET_PLAYER(ePlayer).AI_getAlignmentAttitude(eTarge tPlayer);
if ((iPass == 0) ? (iAttitudeChange > 0) : (iAttitudeChange < 0))
{
if (GC.getLeaderHeadInfo(GET_PLAYER(eTargetPlayer).ge tPersonalityType()).getAlignment() > 0)
{
szTempBuffer.Format(SETCOLR L"%s" ENDCOLR, TEXT_COLOR((iAttitudeChange > 0) ? "COLOR_POSITIVE_TEXT" : "COLOR_NEGATIVE_TEXT"), gDLL->getText("TXT_KEY_MISC_ATTITUDE_GOOD_ALIGNMENT", iAttitudeChange).GetCString());
szBuffer.append(NEWLINE);
szBuffer.append(szTempBuffer);
}
else if (GC.getLeaderHeadInfo(GET_PLAYER(eTargetPlayer).ge tPersonalityType()).getAlignment() < 0)
{
szTempBuffer.Format(SETCOLR L"%s" ENDCOLR, TEXT_COLOR((iAttitudeChange > 0) ? "COLOR_POSITIVE_TEXT" : "COLOR_NEGATIVE_TEXT"), gDLL->getText("TXT_KEY_MISC_ATTITUDE_EVIL_ALIGNMENT", iAttitudeChange).GetCString());
szBuffer.append(NEWLINE);
szBuffer.append(szTempBuffer);
}
else
{
szTempBuffer.Format(SETCOLR L"%s" ENDCOLR, TEXT_COLOR((iAttitudeChange > 0) ? "COLOR_POSITIVE_TEXT" : "COLOR_NEGATIVE_TEXT"), gDLL->getText("TXT_KEY_MISC_ATTITUDE_NEUTRAL_ALIGNMENT", iAttitudeChange).GetCString());
szBuffer.append(NEWLINE);
szBuffer.append(szTempBuffer);
}

}


(I added this into the loop for (iPass = 0; iPass < 2; iPass++) { near the end of it, after all the other similar iAttitudeChange things, just before the nested loop for (iI = 0; iI < NUM_MEMORY_TYPES; ++iI) { starts).

Note: I used szBuffer += NEWLINE + szTempBuffer; at first; but I got compile error: something about different variable types, I suppose += operation couldn't be done because szBuffer and NEWLINE are of different variable types or so. Then I tried szBuffer.append(NEWLINE); szBuffer.append(szTempBuffer); as in other similar instances, and it compiled without errors.


Civ4GameText_DiplomacyText_BTS.xml


<TEXT>
<Tag>TXT_KEY_MISC_ATTITUDE_GOOD_ALIGNMENT</Tag>
<English>%D1: "You are good"</English>
</TEXT>
<TEXT>
<Tag>TXT_KEY_MISC_ATTITUDE_EVIL_ALIGNMENT</Tag>
<English>%D1: "You are evil"</English>
</TEXT>
<TEXT>
<Tag>TXT_KEY_MISC_ATTITUDE_NEUTRAL_ALIGNMENT</Tag>
<English>%D1: "You are neutral"</English>
</TEXT>



Compiles without any problems, game runs without any problems, just I don't see leader attitudes, like +2: "you are good". As a matter of fact, I see no attitudes at all, even +1: "years of peace.....". But when I declare war then I see both "you declared war" and "this war spoils our...". I did numerous testings with different civilizations and it's always the same. Though I never made more then like 20 - 30 turns, I think that's pretty much enough to get at least +1: "years of peace...". And if it's not, anyway I need to have that Alignment attitude kicking in as soon as 2 civs meet.

Kael
Aug 31, 2007, 08:39 PM
Very odd. I dont see any isues with the code you posted. Could you post your entire CvGameTextMgr::getAttitudeString function so I can check it out, that sounds to be where your issue is occuring.

BTW, please put code inside [ code ] [ /code ] (without the spaces) tags to make it easier to read.

zulu9812
Sep 13, 2007, 08:54 PM
I'm trying to add an attitude modifer based on if the other civ has researched a certain technology. I'm stumbling at part 3 of the tutorial in the opening post. Kael's examples use access to a bonus and civic choices - but what code would be used for a tech?

Duke176
Sep 27, 2007, 06:02 PM
hey Kael, how are you?
It's been quite a long time since last time.
I was curious, have been able to solve my prob in the end?
the prb posted in this thread...

Hello to all of you :)

CornPlanter
Sep 29, 2007, 12:03 PM
Very odd. I dont see any isues with the code you posted. Could you post your entire CvGameTextMgr::getAttitudeString function so I can check it out, that sounds to be where your issue is occuring.


Here:

void CvGameTextMgr::getAttitudeString(CvWStringBuffer& szBuffer, PlayerTypes ePlayer, PlayerTypes eTargetPlayer)
{
CvWString szTempBuffer;
int iAttitudeChange;
int iPass;
int iI;
CvPlayerAI& kPlayer = GET_PLAYER(ePlayer);
TeamTypes eTeam = (TeamTypes) kPlayer.getTeam();
CvTeamAI& kTeam = GET_TEAM(eTeam);

szBuffer.append(gDLL->getText("TXT_KEY_ATTITUDE_TOWARDS", GC.getAttitudeInfo(GET_PLAYER(ePlayer).AI_getAttit ude(eTargetPlayer)).getTextKeyWide(), GET_PLAYER(eTargetPlayer).getNameKey()));

for (int iTeam = 0; iTeam < MAX_TEAMS; iTeam++)
{
CvTeam& kLoopTeam = GET_TEAM((TeamTypes)iTeam);
if (kLoopTeam.isAlive())
{
if (NO_PLAYER != eTargetPlayer)
{
CvTeam& kTargetTeam = GET_TEAM(GET_PLAYER(eTargetPlayer).getTeam());
if (kTargetTeam.isHasMet((TeamTypes)iTeam))
{
if (kTeam.isVassal((TeamTypes)iTeam))
{
szBuffer.append(NEWLINE);
szBuffer.append(gDLL->getText("TXT_KEY_ATTITUDE_VASSAL_OF", kLoopTeam.getName().GetCString()));

setVassalRevoltHelp(szBuffer, (TeamTypes)iTeam, kTeam.getID());
}
else if (kLoopTeam.isVassal(kTeam.getID()))
{
szBuffer.append(NEWLINE);
szBuffer.append(gDLL->getText("TXT_KEY_ATTITUDE_MASTER_OF", kLoopTeam.getName().GetCString()));
}
}
}
}
}

for (iPass = 0; iPass < 2; iPass++)
{
iAttitudeChange = GET_PLAYER(ePlayer).AI_getAlignmentAttitude(eTarge tPlayer);
if ((iPass == 0) ? (iAttitudeChange > 0) : (iAttitudeChange < 0))
{
if (GC.getLeaderHeadInfo(GET_PLAYER(eTargetPlayer).ge tPersonalityType()).getAlignment() > 0)
{
szTempBuffer.Format(SETCOLR L"%s" ENDCOLR, TEXT_COLOR((iAttitudeChange > 0) ? "COLOR_POSITIVE_TEXT" : "COLOR_NEGATIVE_TEXT"), gDLL->getText("TXT_KEY_MISC_ATTITUDE_GOOD_ALIGNMENT", iAttitudeChange).GetCString());
szBuffer.append(NEWLINE);
szBuffer.append(szTempBuffer);
}
else if (GC.getLeaderHeadInfo(GET_PLAYER(eTargetPlayer).ge tPersonalityType()).getAlignment() < 0)
{
szTempBuffer.Format(SETCOLR L"%s" ENDCOLR, TEXT_COLOR((iAttitudeChange > 0) ? "COLOR_POSITIVE_TEXT" : "COLOR_NEGATIVE_TEXT"), gDLL->getText("TXT_KEY_MISC_ATTITUDE_EVIL_ALIGNMENT", iAttitudeChange).GetCString());
szBuffer.append(NEWLINE);
szBuffer.append(szTempBuffer);
}
else
{
szTempBuffer.Format(SETCOLR L"%s" ENDCOLR, TEXT_COLOR((iAttitudeChange > 0) ? "COLOR_POSITIVE_TEXT" : "COLOR_NEGATIVE_TEXT"), gDLL->getText("TXT_KEY_MISC_ATTITUDE_NEUTRAL_ALIGNMENT" , iAttitudeChange).GetCString());
szBuffer.append(NEWLINE);
szBuffer.append(szTempBuffer);
}

}

iAttitudeChange = kPlayer.AI_getCloseBordersAttitude(eTargetPlayer);
if ((iPass == 0) ? (iAttitudeChange > 0) : (iAttitudeChange < 0))
{
szTempBuffer.Format(SETCOLR L"%s" ENDCOLR, TEXT_COLOR((iAttitudeChange > 0) ? "COLOR_POSITIVE_TEXT" : "COLOR_NEGATIVE_TEXT"), gDLL->getText("TXT_KEY_MISC_ATTITUDE_LAND_TARGET", iAttitudeChange).GetCString());
szBuffer.append(NEWLINE);
szBuffer.append(szTempBuffer);
}

iAttitudeChange = kPlayer.AI_getWarAttitude(eTargetPlayer);
if ((iPass == 0) ? (iAttitudeChange > 0) : (iAttitudeChange < 0))
{
szTempBuffer.Format(SETCOLR L"%s" ENDCOLR, TEXT_COLOR((iAttitudeChange > 0) ? "COLOR_POSITIVE_TEXT" : "COLOR_NEGATIVE_TEXT"), gDLL->getText("TXT_KEY_MISC_ATTITUDE_WAR", iAttitudeChange).GetCString());
szBuffer.append(NEWLINE);
szBuffer.append(szTempBuffer);
}

iAttitudeChange = kPlayer.AI_getPeaceAttitude(eTargetPlayer);
if ((iPass == 0) ? (iAttitudeChange > 0) : (iAttitudeChange < 0))
{
szTempBuffer.Format(SETCOLR L"%s" ENDCOLR, TEXT_COLOR((iAttitudeChange > 0) ? "COLOR_POSITIVE_TEXT" : "COLOR_NEGATIVE_TEXT"), gDLL->getText("TXT_KEY_MISC_ATTITUDE_PEACE", iAttitudeChange).GetCString());
szBuffer.append(NEWLINE);
szBuffer.append(szTempBuffer);
}

iAttitudeChange = kPlayer.AI_getSameReligionAttitude(eTargetPlayer);
if ((iPass == 0) ? (iAttitudeChange > 0) : (iAttitudeChange < 0))
{
szTempBuffer.Format(SETCOLR L"%s" ENDCOLR, TEXT_COLOR((iAttitudeChange > 0) ? "COLOR_POSITIVE_TEXT" : "COLOR_NEGATIVE_TEXT"), gDLL->getText("TXT_KEY_MISC_ATTITUDE_SAME_RELIGION", iAttitudeChange).GetCString());
szBuffer.append(NEWLINE);
szBuffer.append(szTempBuffer);
}

iAttitudeChange = kPlayer.AI_getDifferentReligionAttitude(eTargetPla yer);
if ((iPass == 0) ? (iAttitudeChange > 0) : (iAttitudeChange < 0))
{
szTempBuffer.Format(SETCOLR L"%s" ENDCOLR, TEXT_COLOR((iAttitudeChange > 0) ? "COLOR_POSITIVE_TEXT" : "COLOR_NEGATIVE_TEXT"), gDLL->getText("TXT_KEY_MISC_ATTITUDE_DIFFERENT_RELIGION", iAttitudeChange).GetCString());
szBuffer.append(NEWLINE);
szBuffer.append(szTempBuffer);
}

iAttitudeChange = kPlayer.AI_getBonusTradeAttitude(eTargetPlayer);
if ((iPass == 0) ? (iAttitudeChange > 0) : (iAttitudeChange < 0))
{
szTempBuffer.Format(SETCOLR L"%s" ENDCOLR, TEXT_COLOR((iAttitudeChange > 0) ? "COLOR_POSITIVE_TEXT" : "COLOR_NEGATIVE_TEXT"), gDLL->getText("TXT_KEY_MISC_ATTITUDE_BONUS_TRADE", iAttitudeChange).GetCString());
szBuffer.append(NEWLINE);
szBuffer.append(szTempBuffer);
}

iAttitudeChange = kPlayer.AI_getOpenBordersAttitude(eTargetPlayer);
if ((iPass == 0) ? (iAttitudeChange > 0) : (iAttitudeChange < 0))
{
szTempBuffer.Format(SETCOLR L"%s" ENDCOLR, TEXT_COLOR((iAttitudeChange > 0) ? "COLOR_POSITIVE_TEXT" : "COLOR_NEGATIVE_TEXT"), gDLL->getText("TXT_KEY_MISC_ATTITUDE_OPEN_BORDERS", iAttitudeChange).GetCString());
szBuffer.append(NEWLINE);
szBuffer.append(szTempBuffer);
}

iAttitudeChange = kPlayer.AI_getDefensivePactAttitude(eTargetPlayer) ;
if ((iPass == 0) ? (iAttitudeChange > 0) : (iAttitudeChange < 0))
{
szTempBuffer.Format(SETCOLR L"%s" ENDCOLR, TEXT_COLOR((iAttitudeChange > 0) ? "COLOR_POSITIVE_TEXT" : "COLOR_NEGATIVE_TEXT"), gDLL->getText("TXT_KEY_MISC_ATTITUDE_DEFENSIVE_PACT", iAttitudeChange).GetCString());
szBuffer.append(NEWLINE);
szBuffer.append(szTempBuffer);
}

iAttitudeChange = kPlayer.AI_getRivalDefensivePactAttitude(eTargetPl ayer);
if ((iPass == 0) ? (iAttitudeChange > 0) : (iAttitudeChange < 0))
{
szTempBuffer.Format(SETCOLR L"%s" ENDCOLR, TEXT_COLOR((iAttitudeChange > 0) ? "COLOR_POSITIVE_TEXT" : "COLOR_NEGATIVE_TEXT"), gDLL->getText("TXT_KEY_MISC_ATTITUDE_RIVAL_DEFENSIVE_PACT", iAttitudeChange).GetCString());
szBuffer.append(NEWLINE);
szBuffer.append(szTempBuffer);
}

iAttitudeChange = kPlayer.AI_getRivalVassalAttitude(eTargetPlayer);
if ((iPass == 0) ? (iAttitudeChange > 0) : (iAttitudeChange < 0))
{
szTempBuffer.Format(SETCOLR L"%s" ENDCOLR, TEXT_COLOR((iAttitudeChange > 0) ? "COLOR_POSITIVE_TEXT" : "COLOR_NEGATIVE_TEXT"), gDLL->getText("TXT_KEY_MISC_ATTITUDE_RIVAL_VASSAL", iAttitudeChange).GetCString());
szBuffer.append(NEWLINE);
szBuffer.append(szTempBuffer);
}

iAttitudeChange = kPlayer.AI_getShareWarAttitude(eTargetPlayer);
if ((iPass == 0) ? (iAttitudeChange > 0) : (iAttitudeChange < 0))
{
szTempBuffer.Format(SETCOLR L"%s" ENDCOLR, TEXT_COLOR((iAttitudeChange > 0) ? "COLOR_POSITIVE_TEXT" : "COLOR_NEGATIVE_TEXT"), gDLL->getText("TXT_KEY_MISC_ATTITUDE_SHARE_WAR", iAttitudeChange).GetCString());
szBuffer.append(NEWLINE);
szBuffer.append(szTempBuffer);
}

iAttitudeChange = kPlayer.AI_getFavoriteCivicAttitude(eTargetPlayer) ;
if ((iPass == 0) ? (iAttitudeChange > 0) : (iAttitudeChange < 0))
{
szTempBuffer.Format(SETCOLR L"%s" ENDCOLR, TEXT_COLOR((iAttitudeChange > 0) ? "COLOR_POSITIVE_TEXT" : "COLOR_NEGATIVE_TEXT"), gDLL->getText("TXT_KEY_MISC_ATTITUDE_FAVORITE_CIVIC", iAttitudeChange).GetCString());
szBuffer.append(NEWLINE);
szBuffer.append(szTempBuffer);
}

iAttitudeChange = kPlayer.AI_getTradeAttitude(eTargetPlayer);
if ((iPass == 0) ? (iAttitudeChange > 0) : (iAttitudeChange < 0))
{
szTempBuffer.Format(SETCOLR L"%s" ENDCOLR, TEXT_COLOR((iAttitudeChange > 0) ? "COLOR_POSITIVE_TEXT" : "COLOR_NEGATIVE_TEXT"), gDLL->getText("TXT_KEY_MISC_ATTITUDE_TRADE", iAttitudeChange).GetCString());
szBuffer.append(NEWLINE);
szBuffer.append(szTempBuffer);
}

iAttitudeChange = kPlayer.AI_getRivalTradeAttitude(eTargetPlayer);
if ((iPass == 0) ? (iAttitudeChange > 0) : (iAttitudeChange < 0))
{
szTempBuffer.Format(SETCOLR L"%s" ENDCOLR, TEXT_COLOR((iAttitudeChange > 0) ? "COLOR_POSITIVE_TEXT" : "COLOR_NEGATIVE_TEXT"), gDLL->getText("TXT_KEY_MISC_ATTITUDE_RIVAL_TRADE", iAttitudeChange).GetCString());
szBuffer.append(NEWLINE);
szBuffer.append(szTempBuffer);
}

iAttitudeChange = GET_PLAYER(ePlayer).AI_getColonyAttitude(eTargetPl ayer);
if ((iPass == 0) ? (iAttitudeChange > 0) : (iAttitudeChange < 0))
{
szTempBuffer.Format(SETCOLR L"%s" ENDCOLR, TEXT_COLOR((iAttitudeChange > 0) ? "COLOR_POSITIVE_TEXT" : "COLOR_NEGATIVE_TEXT"), gDLL->getText("TXT_KEY_MISC_ATTITUDE_FREEDOM", iAttitudeChange).GetCString());
szBuffer.append(NEWLINE);
szBuffer.append(szTempBuffer);
}

iAttitudeChange = GET_PLAYER(ePlayer).AI_getAttitudeExtra(eTargetPla yer);
if ((iPass == 0) ? (iAttitudeChange > 0) : (iAttitudeChange < 0))
{
szTempBuffer.Format(SETCOLR L"%s" ENDCOLR, TEXT_COLOR((iAttitudeChange > 0) ? "COLOR_POSITIVE_TEXT" : "COLOR_NEGATIVE_TEXT"), gDLL->getText(((iAttitudeChange > 0) ? "TXT_KEY_MISC_ATTITUDE_EXTRA_GOOD" : "TXT_KEY_MISC_ATTITUDE_EXTRA_BAD"), iAttitudeChange).GetCString());
szBuffer.append(NEWLINE);
szBuffer.append(szTempBuffer);
}

for (iI = 0; iI < NUM_MEMORY_TYPES; ++iI)
{
iAttitudeChange = kPlayer.AI_getMemoryAttitude(eTargetPlayer, ((MemoryTypes)iI));
if ((iPass == 0) ? (iAttitudeChange > 0) : (iAttitudeChange < 0))
{
szTempBuffer.Format(SETCOLR L"%s" ENDCOLR, TEXT_COLOR((iAttitudeChange > 0) ? "COLOR_POSITIVE_TEXT" : "COLOR_NEGATIVE_TEXT"), gDLL->getText("TXT_KEY_MISC_ATTITUDE_MEMORY", iAttitudeChange, GC.getMemoryInfo((MemoryTypes)iI).getDescription() ).GetCString());
szBuffer.append(NEWLINE);
szBuffer.append(szTempBuffer);
}
}
}

if (NO_PLAYER != eTargetPlayer)
{
int iWarWeariness = GET_PLAYER(eTargetPlayer).getModifiedWarWearinessP ercentAnger(GET_TEAM(GET_PLAYER(eTargetPlayer).get Team()).getWarWeariness(eTeam) * max(0, 100 + kTeam.getEnemyWarWearinessModifier()));
if (iWarWeariness / 10000 > 0)
{
szBuffer.append(NEWLINE);
szBuffer.append(gDLL->getText("TXT_KEY_WAR_WEAR_HELP", iWarWeariness / 10000));
}
}

}

Duke176
Oct 01, 2007, 07:43 PM
do you think that my prb could be this:

when I make the final calculation of OilTotalConsumption I set it as Integer, but I've seen that in my function:


//get buildings oil consumption per turn
int CvCity::OilConsumptionPerBuildings()
{

int iAirportCons = 0;
int iDryDockCons = 0;
int iHospitalCons = 0;
int iHydroplantCons = 0;
int iNuclearPlantCons = 0;
int iRecyclingCenterCons = 0;

int iTotalConsumption = 0;
// basic city consumptio BCC from Define INT
int iBCC = 0;
// int iMedCitiesConsumption = 0;
// int iBigCitiesConsumption = 0;
// int iEnormousCitiesConsumption = 0;


if (hasActiveBuilding((BuildingTypes)GC.getInfoTypeFo rString("BUILDING_AIRPORT")))
{
iAirportCons = GC.getBuildingInfo(BuildingTypes (GC.getInfoTypeForString("BUILDING_AIRPORT"))).getOilConsumption();
}
return iAirportCons;

if (hasActiveBuilding((BuildingTypes)GC.getInfoTypeFo rString("BUILDING_DRYDOCK")))
{
iDryDockCons = GC.getBuildingInfo(BuildingTypes (GC.getInfoTypeForString("BUILDING_DRYDOCK"))).getOilConsumption();
}
return iDryDockCons;

if (hasActiveBuilding((BuildingTypes)GC.getInfoTypeFo rString("BUILDING_HOSPITAL")))
{
iHospitalCons = GC.getBuildingInfo(BuildingTypes (GC.getInfoTypeForString("BUILDING_HOSPITAL"))).getOilConsumption();
}
return iHospitalCons;

if (hasActiveBuilding((BuildingTypes)GC.getInfoTypeFo rString("BUILDING_HYDROPLANT")))
{
iHydroplantCons = GC.getBuildingInfo(BuildingTypes (GC.getInfoTypeForString("BUILDING_HYDROPLANT"))).getOilConsumption();
}
return iHydroplantCons;

if (hasActiveBuilding((BuildingTypes)GC.getInfoTypeFo rString("BUILDING_NUCLEAR_PLANT")))
{
iNuclearPlantCons = GC.getBuildingInfo(BuildingTypes (GC.getInfoTypeForString("BUILDING_NUCLEAR_PLANT"))).getOilConsumption();
}
return iNuclearPlantCons;

if (hasActiveBuilding((BuildingTypes)GC.getInfoTypeFo rString("BUILDING_RECYCLING_CENTER")))
{
iRecyclingCenterCons = GC.getBuildingInfo(BuildingTypes (GC.getInfoTypeForString("BUILDING_RECYCLING_CENTER"))).getOilConsumption();
}
return iRecyclingCenterCons;

// let's go for population
if ((getPopulation() >= 1) && (getPopulation() <= 5))
{
iBCC = (GC.getDefineINT("LITTLE_CITIES_CONSUMPTION"));
}
else if ((getPopulation() >= 6) && (getPopulation() <= 10))
{
iBCC = (GC.getDefineINT("MED_CITIES_CONSUMPTION"));
}
else if ((getPopulation() >= 11) && (getPopulation() <= 15))
{
iBCC = (GC.getDefineINT("BIG_CITIES_CONSUMPTION"));
}
else if (getPopulation() >= 16)
{
iBCC = (GC.getDefineINT("ENORMOUS_CITIES_CONSUMPTION"));
}
return iBCC;



iTotalConsumption = ((iBCC * getPopulation()) + ((iBCC * iAirportCons) / 100) + ((iBCC * iDryDockCons) / 100) + ((iBCC * iHospitalCons) / 100) + ((iBCC * iHydroplantCons) / 100) + ((iBCC * iNuclearPlantCons) / 100) + ((iBCC * iRecyclingCenterCons) / 100));


return iTotalConsumption;

}


I make the resoult divided by 100, so could happens that the resoult is a 0,xxx number.
Do you think compiler get this prb and stops it self?

Duke176
Oct 01, 2007, 07:47 PM
I mean look here I set the XML file like this:

<BuildingClass>BUILDINGCLASS_RECYCLING_CENTER</BuildingClass>
<Type>BUILDING_RECYCLING_CENTER</Type>
<SpecialBuildingType>NONE</SpecialBuildingType>
<Description>TXT_KEY_BUILDING_RECYCLING_CENTER</Description>
<Civilopedia>TXT_KEY_BUILDING_RECYCLING_CENTER_PEDIA</Civilopedia>
<Strategy>TXT_KEY_BUILDING_RECYCLING_CENTER_STRATEGY</Strategy>
<Advisor>ADVISOR_GROWTH</Advisor>
<ArtDefineTag>ART_DEF_BUILDING_RECYCLING_CENTER</ArtDefineTag>
<MovieDefineTag>NONE</MovieDefineTag>
<HolyCity>NONE</HolyCity>
<ReligionType>NONE</ReligionType>
<StateReligion>NONE</StateReligion>
<PrereqReligion>NONE</PrereqReligion>
<GlobalReligionCommerce>NONE</GlobalReligionCommerce>
<VictoryPrereq>NONE</VictoryPrereq>
<FreeStartEra>NONE</FreeStartEra>
<MaxStartEra>NONE</MaxStartEra>
<ObsoleteTech>NONE</ObsoleteTech>
<PrereqTech>TECH_ECOLOGY</PrereqTech>
<TechTypes/>
<Bonus>NONE</Bonus>
<PrereqBonuses/>
<ProductionTraits/>
<HappinessTraits/>
<PowerBonus>NONE</PowerBonus>
<FreeBonus>NONE</FreeBonus>
<iNumFreeBonuses>0</iNumFreeBonuses>
<FreeBuilding>NONE</FreeBuilding>
<FreePromotion>NONE</FreePromotion>
<CivicOption>NONE</CivicOption>
<GreatPeopleUnitClass>NONE</GreatPeopleUnitClass>
<iGreatPeopleRateChange>0</iGreatPeopleRateChange>
<iHurryAngerModifier>0</iHurryAngerModifier>
<bBorderObstacle>0</bBorderObstacle>
<bTeamShare>0</bTeamShare>
<bWater>0</bWater>
<bRiver>0</bRiver>
<bPower>0</bPower>
<bDirtyPower>0</bDirtyPower>
<bAreaCleanPower>0</bAreaCleanPower>
<bDiploVote>0</bDiploVote>
<bForceTeamVoteEligible>0</bForceTeamVoteEligible>
<bCapital>0</bCapital>
<bGovernmentCenter>0</bGovernmentCenter>
<bGoldenAge>0</bGoldenAge>
<bMapCentering>0</bMapCentering>
<bNoUnhappiness>0</bNoUnhappiness>
<bNoUnhealthyPopulation>0</bNoUnhealthyPopulation>
<bBuildingOnlyHealthy>1</bBuildingOnlyHealthy>
<bNeverCapture>0</bNeverCapture>
<bNukeImmune>0</bNukeImmune>
<bPrereqReligion>0</bPrereqReligion>
<bCenterInCity>0</bCenterInCity>
<iAIWeight>0</iAIWeight>
<iCost>300</iCost>
<iHurryCostModifier>0</iHurryCostModifier>
<iMinAreaSize>-1</iMinAreaSize>
<iConquestProb>66</iConquestProb>
<iCitiesPrereq>0</iCitiesPrereq>
<iTeamsPrereq>0</iTeamsPrereq>
<iLevelPrereq>0</iLevelPrereq>
<iMinLatitude>0</iMinLatitude>
<iMaxLatitude>90</iMaxLatitude>
<iGreatPeopleRateModifier>0</iGreatPeopleRateModifier>
<iGreatGeneralRateModifier>0</iGreatGeneralRateModifier>
<iDomesticGreatGeneralRateModifier>0</iDomesticGreatGeneralRateModifier>
<iGlobalGreatPeopleRateModifier>0</iGlobalGreatPeopleRateModifier>
<iAnarchyModifier>0</iAnarchyModifier>
<iGlobalHurryModifier>0</iGlobalHurryModifier>
<iExperience>0</iExperience>
<iGlobalExperience>0</iGlobalExperience>
<iFoodKept>0</iFoodKept>
<!-- OIL CONSUMPTION -->
<iOilConsumption>-40</iOilConsumption>
<!-- END -->
<iAirlift>0</iAirlift>
<iAirModifier>0</iAirModifier>
<iNukeModifier>0</iNukeModifier>
<iNukeExplosionRand>0</iNukeExplosionRand>
<iFreeSpecialist>0</iFreeSpecialist>
<iAreaFreeSpecialist>0</iAreaFreeSpecialist>
<iGlobalFreeSpecialist>0</iGlobalFreeSpecialist>
<iMaintenanceModifier>0</iMaintenanceModifier>
<iWarWearinessModifier>0</iWarWearinessModifier>
<iGlobalWarWearinessModifier>0</iGlobalWarWearinessModifier>
<iHealRateChange>0</iHealRateChange>
<iHealth>0</iHealth>
<iAreaHealth>0</iAreaHealth>
<iGlobalHealth>0</iGlobalHealth>
<iHappiness>0</iHappiness>
<iAreaHappiness>0</iAreaHappiness>
<iGlobalHappiness>0</iGlobalHappiness>
<iStateReligionHappiness>0</iStateReligionHappiness>
<iWorkerSpeedModifier>0</iWorkerSpeedModifier>
<iMilitaryProductionModifier>0</iMilitaryProductionModifier>
<iSpaceProductionModifier>0</iSpaceProductionModifier>
<iGlobalSpaceProductionModifier>0</iGlobalSpaceProductionModifier>
<iTradeRoutes>0</iTradeRoutes>
<iCoastalTradeRoutes>0</iCoastalTradeRoutes>
<iGlobalTradeRoutes>0</iGlobalTradeRoutes>
<iTradeRouteModifier>0</iTradeRouteModifier>
<iGlobalPopulationChange>0</iGlobalPopulationChange>
<iFreeTechs>0</iFreeTechs>
<iDefense>0</iDefense>
<iBombardDefense>0</iBombardDefense>
<iAllCityDefense>0</iAllCityDefense>
<iAsset>6</iAsset>
<iPower>0</iPower>
<fVisibilityPriority>1.0</fVisibilityPriority>
<SeaPlotYieldChanges/>
<GlobalSeaPlotYieldChanges/>
<YieldChanges/>
<CommerceChanges/>
<ObsoleteSafeCommerceChanges/>
<CommerceChangeDoubleTimes/>
<CommerceModifiers/>
<GlobalCommerceModifiers/>
<SpecialistExtraCommerces/>
<StateReligionCommerces/>
<CommerceHappinesses/>
<ReligionChanges/>
<SpecialistCounts/>
<FreeSpecialistCounts/>
<CommerceFlexibles/>
<CommerceChangeOriginalOwners/>
<ConstructSound>AS2D_BUILD_RECYCLING_CENTER</ConstructSound>
<BonusHealthChanges/>
<BonusHappinessChanges/>
<BonusProductionModifiers/>
<UnitCombatFreeExperiences/>
<DomainFreeExperiences/>
<DomainProductionModifiers/>
<BuildingHappinessChanges/>
<PrereqBuildingClasses/>
<BuildingClassNeededs/>
<SpecialistYieldChanges/>
<BonusYieldModifiers/>
<Flavors>
<Flavor>
<FlavorType>FLAVOR_GROWTH</FlavorType>
<iFlavor>10</iFlavor>
</Flavor>
</Flavors>
<HotKey/>
<bAltDown>0</bAltDown>
<bShiftDown>0</bShiftDown>
<bCtrlDown>0</bCtrlDown>
<iHotKeyPriority>0</iHotKeyPriority>
</BuildingInfo>


so here it would be for ex. iBCC = 30
30 * -40 = -120
-120/100 = - 1,2

2nd) I should probably give him all the buildings with a certain ammount of OIL Consumption or when it doens't find the value he cannot associate the absence with "0". Correct?

3rd) isn't there a faster way to loop throw the buildings and get all the building active OIL CONSUMPTION value?

Duke176
Oct 03, 2007, 04:14 PM
so ladies and gentlemen I solved the prb:
if you want to add a new attribute to XML and get into .cpp you should add it after last int attribute you see.
In the example above


<Flavors>
<Flavor>
<FlavorType>FLAVOR_GROWTH</FlavorType>
<iFlavor>10</iFlavor>
</Flavor>
</Flavors>
<HotKey/>
<bAltDown>0</bAltDown>
<bShiftDown>0</bShiftDown>
<bCtrlDown>0</bCtrlDown>
<iHotKeyPriority>0</iHotKeyPriority>

HERE
</BuildingInfo>


So it will load the MOD!
This idiot problem stopped me 1 year ago...
xxxxxing XML... (sorry)

Kael
Oct 05, 2007, 07:54 AM
Great to hear you have it worked out. I have inserted attributes at places other than the end of the object definition without issues so Im not sure why that fixed it for you, but either way at least its working! :D

Duke176
Oct 05, 2007, 02:50 PM
yes that was great that prb made me crazy. :)
never happened to me before too.
Anyway I'm moving on now ;)

CornPlanter
Oct 05, 2007, 05:09 PM
Any ideas about my issue? :(

Duke176
Oct 05, 2007, 08:49 PM
oyur prb is with compiling or with XML load?

if the second remeber to add in your xmlschema file


<element type="iAlignment" minOccurs="0"/>

or

insert your new attribute fro each Civ you find.



if you don't do this the game won't know what to do if the attribute is absent.
with minOccurs you tell it: "if you don't find it assign basic value = 0"

deanej
Oct 08, 2007, 10:12 PM
Is there a way to expose a completely new XML file to the SDK? I was hoping to make different types of cloaking devices in my Star Trek mod by making a CloakingInfos file and adding it to the SDK. I was hoping to have someone else do it, but I have to learn C++ and do it myself because nobody is interested.

CornPlanter
Oct 08, 2007, 10:21 PM
oyur prb is with compiling or with XML load?

if the second remeber to add in your xmlschema file


<element type="iAlignment" minOccurs="0"/>

or

insert your new attribute fro each Civ you find.



if you don't do this the game won't know what to do if the attribute is absent.
with minOccurs you tell it: "if you don't find it assign basic value = 0"

I did assign it to every civ. I don't know where exactly the problem is. I dont see any error messages, it just doesn't work.

PeteT
Oct 09, 2007, 09:11 AM
In CvLeaderHeadInfo::read(CvXMLLoadUtility* pXML), try moving "pXML->GetChildXmlValByName(&m_iAlignment, "iAlignment");" up from


pXML->GetChildXmlValByName(&m_iDifferentReligionAttitud eChangeLimit, "iDifferentReligionAttitudeChangeLimit");
pXML->GetChildXmlValByName(&m_iAlignment, "iAlignment");
pXML->GetChildXmlValByName(&m_iBonusTradeAttitudeDiviso r, "iBonusTradeAttitudeDivisor");


to:


setArtDefineTag(szTextVal);
pXML->GetChildXmlValByName(&m_iAlignment, "iAlignment");
pXML->GetChildXmlValByName(&m_iWonderConstructRand, "iWonderConstructRand");


edit: @deanej, you can find an example of this in http://forums.civfanatics.com/showthread.php?t=245261

deanej
Oct 09, 2007, 09:04 PM
^How exactly does the SDK know what file to use when getting the variables? Does a new file have to be defined somewhere else than CvXMLLoadUtilitySet.cpp?

CornPlanter
Oct 10, 2007, 01:42 AM
Edit:

The problem really lies in displaying text because attitude change works properly as soon as I meet another civ. Those who must be pleasant are pleasant and those who must be cautious are cautious etc. etc. Also I can see texts for years of peace etc. now. I will try to look into that textmngr file again probably I'll find some clue.

deanej
Oct 21, 2007, 05:13 PM
How do I add XML tags that are linked? Example:

<FeatureModify>
<FeatureType>
<Feature>FEATURE_FOREST</Feature>
<iMovement>1</iMovement>
<bImpassible>0</bImpassible>
</FeatureType>
<FeatureType>
<Feature>FEATURE_ICE</Feature>
<iMovement>1</iMovement>
<bImpassible>0</bImpassible>
</FeatureType>
</FeatureModify>

and

<CivRequire>
<Civilization>CIVILIZATION_KLINGON</Civilization>
<Civilization>CIVILIZATION_ROMULAN</Civilization>
</CivRequire>

deanej
Dec 02, 2007, 02:47 PM
When I add things to CvInfos.cpp and CvInfos.h, do the variables have to be in the order that they are in the XML file? They don't seem to be in the file, yet they are for the tutorial.

Shqype
Mar 05, 2008, 03:48 PM
Bump. This is useful, so it should be added to woodelf's Tutorial Thread Index.

Shqype
Mar 14, 2008, 10:12 PM
Kael:

Would it be possible for you to update this excellent tutorial with a section on exposing newly created tags to Python? It would be very helpful :)

cephalo
May 15, 2008, 06:36 PM
Ok, I'm having some trouble here.

I'm adding some tags so that a building can add happiness per each of a certain kind of improvement in the city radius. The xml structure is exactly the same as the tag <ImprovementFreeSpecialists> that is used by the National Park building. Here is an example:


<ImprovementHappinessTypes>
<ImprovementHappiness>
<ImprovementType>IMPROVEMENT_VILLAGE</ImprovementType>
<iHappinessCount>1</iHappinessCount>
</ImprovementHappiness>
<ImprovementHappiness>
<ImprovementType>IMPROVEMENT_TOWN</ImprovementType>
<iHappinessCount>1</iHappinessCount>
</ImprovementHappiness>
</ImprovementHappinessTypes>


I want one happiness in each town and village, maybe someday 2 per each whatever.

Here are the relevant Schema entries:

<ElementType name="iHappinessCount" content="textOnly" dt:type="int"/>
<ElementType name="ImprovementHappiness" content="eltOnly">
<element type="ImprovementType"/>
<element type="iHappinessCount"/>
</ElementType>



Then I use this to read in the XML:

pXML->SetVariableListTagPair(&m_piImprovementHappiness, "ImprovementHappinessTypes", sizeof(GC.getImprovementInfo((ImprovementTypes)0)) , GC.getNumImprovementInfos());



The XML loads fine and everything compiles fine in the SDK, but when I try to access the data with this function:

int CvBuildingInfo::getImprovementHappiness(int i) const
{
FAssertMsg(i < GC.getNumImprovementInfos(), "Index out of bounds");
FAssertMsg(i > -1, "Index out of bounds");
return m_piImprovementHappiness ? m_piImprovementHappiness[i] : -1;
}



Instead of 0 or 1 I'm getting garbage values like 84672934. All these functions and variables are set up like the <ImprovementFreeSpecialists> tag. I'm in a little over my head with the XML reading functions. I'd like to try skipping the helper function SetVariableListTagPair and read it in long hand, but I'm not sure how the whole scheme works.

cephalo
May 17, 2008, 09:10 AM
I did some more testing and it looks like my XML is in fact loading in correctly. Things go haywire in between the XML reading and my access to the data.

That's pretty scary...
Goodbye weekend...

Ninja2
May 20, 2008, 05:34 AM
Don't you need to have something like this (just picked a random line from CvInfos.XML):

pXML->GetChildXmlValByName(&m_iImprovementUpgradeRateModifier, "iImprovementUpgradeRateModifier");

cephalo
May 20, 2008, 09:03 AM
Don't you need to have something like this (just picked a random line from CvInfos.XML):

pXML->GetChildXmlValByName(&m_iImprovementUpgradeRateModifier, "iImprovementUpgradeRateModifier");

The example I posted above is working. My mistake was elsewhere.

Gooblah
Jan 25, 2009, 07:16 PM
So..let's say I wanted to add a new tag to Civ4UnitsInfo.xml.

1) Add the tag to the Schema
2) Add the tag to all entries in the file
3) Enter Sid Meier's Civilization 4/Beyond the Sword/CvGameCoreDLL/CvUnit.cpp?

I think understand what to do from there, but I'm unsure as to which files to enter..

Edit: in the schema, what does "eltOnly" and "textOnly" mean?

xienwolf
Jan 26, 2009, 01:02 AM
eltOnly means that it contains sub-elements, and won't have any data to itself. TextOnly means it will have data, but won't have any sub-elements.


You have to modify quite a few locations in the DLL to get new tags to work I am afraid.

Trachmyr
Aug 13, 2009, 06:29 PM
First off... thanks for an awesome tutorial, it helped me a great deal.

I'm working on a mod that (for right now) alters the way Values ("Religions") work in the Final Frontier mod. So far I have everything working except that the actual Diplomacy value doesn't print out after the diplomacy text:

It says "We admire your values."
When it's suppose to say "+2: We admire your values."


// TRACHMYR - FFValuesMod - Display new values attitudes in diplomacy
iAttitudeChange = GET_PLAYER(ePlayer).AI_getKnowledgeValueAttitude(e TargetPlayer);
if ((iPass == 0) ? (iAttitudeChange > 0) : (iAttitudeChange < 0))
{
szTempBuffer.Format(SETCOLR L"%s" ENDCOLR, TEXT_COLOR((iAttitudeChange > 0) ? "COLOR_POSITIVE_TEXT" : "COLOR_NEGATIVE_TEXT"), gDLL->getText(((iAttitudeChange > 0) ? "TXT_KEY_MISC_ATTITUDE_VALUE_GOOD" : "TXT_KEY_MISC_ATTITUDE_VALUE_BAD"), iAttitudeChange).GetCString());
szBuffer.append(NEWLINE);
szBuffer.append(szTempBuffer);
}

iAttitudeChange = GET_PLAYER(ePlayer).AI_getPleasureValueAttitude(eT argetPlayer);
if ((iPass == 0) ? (iAttitudeChange > 0) : (iAttitudeChange < 0))
{
szTempBuffer.Format(SETCOLR L"%s" ENDCOLR, TEXT_COLOR((iAttitudeChange > 0) ? "COLOR_POSITIVE_TEXT" : "COLOR_NEGATIVE_TEXT"), gDLL->getText(((iAttitudeChange > 0) ? "TXT_KEY_MISC_ATTITUDE_VALUE_GOOD" : "TXT_KEY_MISC_ATTITUDE_VALUE_BAD"), iAttitudeChange).GetCString());
szBuffer.append(NEWLINE);
szBuffer.append(szTempBuffer);
}

iAttitudeChange = GET_PLAYER(ePlayer).AI_getPowerValueAttitude(eTarg etPlayer);
if ((iPass == 0) ? (iAttitudeChange > 0) : (iAttitudeChange < 0))
{
szTempBuffer.Format(SETCOLR L"%s" ENDCOLR, TEXT_COLOR((iAttitudeChange > 0) ? "COLOR_POSITIVE_TEXT" : "COLOR_NEGATIVE_TEXT"), gDLL->getText(((iAttitudeChange > 0) ? "TXT_KEY_MISC_ATTITUDE_VALUE_GOOD" : "TXT_KEY_MISC_ATTITUDE_VALUE_BAD"), iAttitudeChange).GetCString());
szBuffer.append(NEWLINE);
szBuffer.append(szTempBuffer);
}

iAttitudeChange = GET_PLAYER(ePlayer).AI_getReligionValueAttitude(eT argetPlayer);
if ((iPass == 0) ? (iAttitudeChange > 0) : (iAttitudeChange < 0))
{
szTempBuffer.Format(SETCOLR L"%s" ENDCOLR, TEXT_COLOR((iAttitudeChange > 0) ? "COLOR_POSITIVE_TEXT" : "COLOR_NEGATIVE_TEXT"), gDLL->getText(((iAttitudeChange > 0) ? "TXT_KEY_MISC_ATTITUDE_VALUE_GOOD" : "TXT_KEY_MISC_ATTITUDE_VALUE_BAD"), iAttitudeChange).GetCString());
szBuffer.append(NEWLINE);
szBuffer.append(szTempBuffer);
}

iAttitudeChange = GET_PLAYER(ePlayer).AI_getSurvivalValueAttitude(eT argetPlayer);
if ((iPass == 0) ? (iAttitudeChange > 0) : (iAttitudeChange < 0))
{
szTempBuffer.Format(SETCOLR L"%s" ENDCOLR, TEXT_COLOR((iAttitudeChange > 0) ? "COLOR_POSITIVE_TEXT" : "COLOR_NEGATIVE_TEXT"), gDLL->getText(((iAttitudeChange > 0) ? "TXT_KEY_MISC_ATTITUDE_VALUE_GOOD" : "TXT_KEY_MISC_ATTITUDE_VALUE_BAD"), iAttitudeChange).GetCString());
szBuffer.append(NEWLINE);
szBuffer.append(szTempBuffer);
}

iAttitudeChange = GET_PLAYER(ePlayer).AI_getWealthValueAttitude(eTar getPlayer);
if ((iPass == 0) ? (iAttitudeChange > 0) : (iAttitudeChange < 0))
{
szTempBuffer.Format(SETCOLR L"%s" ENDCOLR, TEXT_COLOR((iAttitudeChange > 0) ? "COLOR_POSITIVE_TEXT" : "COLOR_NEGATIVE_TEXT"), gDLL->getText(((iAttitudeChange > 0) ? "TXT_KEY_MISC_ATTITUDE_VALUE_GOOD" : "TXT_KEY_MISC_ATTITUDE_VALUE_BAD"), iAttitudeChange).GetCString());
szBuffer.append(NEWLINE);
szBuffer.append(szTempBuffer);
}

iAttitudeChange = GET_PLAYER(ePlayer).AI_getNoValueAttitude(eTargetP layer);
if ((iPass == 0) ? (iAttitudeChange > 0) : (iAttitudeChange < 0))
{
szTempBuffer.Format(SETCOLR L"%s" ENDCOLR, TEXT_COLOR((iAttitudeChange > 0) ? "COLOR_POSITIVE_TEXT" : "COLOR_NEGATIVE_TEXT"), gDLL->getText(((iAttitudeChange > 0) ? "TXT_KEY_MISC_ATTITUDE_NO_VALUE_GOOD" : "TXT_KEY_MISC_ATTITUDE_NO_VALUE_BAD"), iAttitudeChange).GetCString());
szBuffer.append(NEWLINE);
szBuffer.append(szTempBuffer);
}
// END


However the game properly recognizes when there is an attitude change, applies it correctly, and prints the text correctly.


<TEXT>
<Tag>TXT_KEY_MISC_ATTITUDE_VALUE_GOOD</Tag>
<English>"We admire your values."</English>
<French>"We admire your values."</French>
<German>"We admire your values."</German>
<Italian>"We admire your values."</Italian>
<Spanish>"We admire your values."</Spanish>
</TEXT>
<TEXT>
<Tag>TXT_KEY_MISC_ATTITUDE_VALUE_BAD</Tag>
<English>"We oppose your values."</English>
<French>"We oppose your values."</French>
<German>"We oppose your values."</German>
<Italian>"We oppose your values."</Italian>
<Spanish>"We oppose your values."</Spanish>
</TEXT>
<TEXT>
<Tag>TXT_KEY_MISC_ATTITUDE_NO_VALUE_GOOD</Tag>
<English>"We appreciate that you are open to all values."</English>
<French>"We appreciate that you are open to all values."</French>
<German>"We appreciate that you are open to all values."</German>
<Italian>"We appreciate that you are open to all values."</Italian>
<Spanish>"We appreciate that you are open to all values."</Spanish>
</TEXT>
<TEXT>
<Tag>TXT_KEY_MISC_ATTITUDE_NO_VALUE_BAD</Tag>
<English>"We are concerned that you do not adhere to any value."</English>
<French>"We are concerned that you do not adhere to any value."</French>
<German>"We are concerned that you do not adhere to any value."</German>
<Italian>"We are concerned that you do not adhere to any value."</Italian>
<Spanish>"We are concerned that you do not adhere to any value."</Spanish>
</TEXT>


Any ideas?

Thanks again.

xienwolf
Aug 14, 2009, 12:25 PM
You need to start each of your text keys with

English>%D: We...</English>

%D inserts the number provided for the text key, and includes a + or - sign (lower case %d won't show the + sign)

Opera
Aug 14, 2009, 05:06 PM
Thanks xienwolf, I didn't know why the + sign was shown... :D

Trachmyr
Aug 15, 2009, 12:35 AM
Thank you very much!

xienwolf
Aug 15, 2009, 11:15 AM
Oops, it should be %D1, you need the integer to specify WHICH of the items provided to the text key to use. Forgot about that bit.

You are also allowed to leave yourself/others a little "note" about what the item should be by using an Underscore and then unspaced text, ie:

%D1_AttitudeValue

danzo07
Feb 07, 2010, 05:37 PM
great game except this mod crap need a real game company making it