Civic Attitude Modifier

Afforess

The White Wizard
Joined
Jul 31, 2007
Messages
12,239
Location
Austin, Texas
I want to see if something that I would like to implement is possible to do in the first place. I want to add attitude modifiers for civics, so that a civic like slavery would result in a "-2" for your relations with all other nations not running slavery. Now that isn't my question, I'm fairly confident I can pull that off. My question is, can I make the diplomacy text that explains the attitude modifier dynamic. When I say "dynamic" I mean different for every civic, and easily changeable in the XML. Here is an example of what I would like the XML for the attitude modifier to look like.

Code:
<CivicAttitudeModifiers>
  <iCivicAttitudeModifier>-2</iCivicAttitudeModifier>
  <CivicDisplayText>TXT_KEY_CIVIC_ATTITUDE_SLAVERY</CivicDisplayText>
</CivicAttitudeModifiers>

This way, different civics could have unique modifiers and unique display text for each one. The Text_Key would be somewhere else, in an XML text file.
 
It looks like you plan to have the message depend only on the "bad" civic and not on what "good" civic the other player is running. For example, if I am running Slavery, every AI will show a -2 modifier towards me with "You whip your population to the bone" as the reason regardless of the civics they are running. Is this correct?

If so what you propose looks like it should be quite doable. Given that each civic can have only one modifier such as this, you don't even need the outer element (though I like it for logically grouping the two related elements), and having it be plural is a bit misleading (there can be only one modifier-message pair per civic).

Going Further

If you want to make it more complicated, you could have a modifier for each civic combination.

  • Tribalism admires Slavery: +1
  • Serfdom mildly dislikes Slavery: -1
  • Caste System dislikes Slavery: -2
  • Emancipation hates Slavery: -4
To do this you would need something like this:

Code:
<CivicAttitudeModifiers>
    <CivicAttitudeModifier>
        <Civic>CIVIC_TRIBALISM</Civic>
        <iModifier>1</iModifier>
        <Reason>TXT_KEY_CIVIC_ATTITUDE_SLAVERY_TRIBALISM</Reason>
    </CivicAttitudeModifier>
    <CivicAttitudeModifier>
        <Civic>CIVIC_SERFDOM</Civic>
        <iModifier>-1</iModifier>
        <Reason>TXT_KEY_CIVIC_ATTITUDE_SLAVERY_SERFDOM</Reason>
    </CivicAttitudeModifier>
    <CivicAttitudeModifier>
        <Civic>CIVIC_CASTE_SYSTEM</Civic>
        <iModifier>-2</iModifier>
        <Reason>TXT_KEY_CIVIC_ATTITUDE_SLAVERY_CASTE_SYSTEM</Reason>
    </CivicAttitudeModifier>
    <CivicAttitudeModifier>
        <Civic>CIVIC_EMANCIPATION</Civic>
        <iModifier>-4</iModifier>
        <Reason>TXT_KEY_CIVIC_ATTITUDE_SLAVERY_EMANCIPATION</Reason>
    </CivicAttitudeModifier>
</CivicAttitudeModifiers>

This shouldn't be that much more difficult to implement, and I like the extra flexibility. Instead of specifying the <Civic> for each <CivicAttitudeModifier>, it may be easier to force the XML to have one <CivicAttitudeModifier> for each civic in that option type (5 in the normal game).

In any case, you'd probably want to code it as two arrays each with <num-civics-per-option-type> elements, one to hold the modifiers, the other to hold the messages. This makes the code easier to write.
 
It looks like you plan to have the message depend only on the "bad" civic and not on what "good" civic the other player is running. For example, if I am running Slavery, every AI will show a -2 modifier towards me with "You whip your population to the bone" as the reason regardless of the civics they are running. Is this correct?

Yes. Exactly.
If so what you propose looks like it should be quite doable. Given that each civic can have only one modifier such as this, you don't even need the outer element (though I like it for logically grouping the two related elements), and having it be plural is a bit misleading (there can be only one modifier-message pair per civic).

Okay. Good.
Going Further

If you want to make it more complicated, you could have a modifier for each civic combination.

  • Tribalism admires Slavery: +1
  • Serfdom mildly dislikes Slavery: -1
  • Caste System dislikes Slavery: -2
  • Emancipation hates Slavery: -4
To do this you would need something like this:

Code:
<CivicAttitudeModifiers>
    <CivicAttitudeModifier>
        <Civic>CIVIC_TRIBALISM</Civic>
        <iModifier>1</iModifier>
        <Reason>TXT_KEY_CIVIC_ATTITUDE_SLAVERY_TRIBALISM</Reason>
    </CivicAttitudeModifier>
    <CivicAttitudeModifier>
        <Civic>CIVIC_SERFDOM</Civic>
        <iModifier>-1</iModifier>
        <Reason>TXT_KEY_CIVIC_ATTITUDE_SLAVERY_SERFDOM</Reason>
    </CivicAttitudeModifier>
    <CivicAttitudeModifier>
        <Civic>CIVIC_CASTE_SYSTEM</Civic>
        <iModifier>-2</iModifier>
        <Reason>TXT_KEY_CIVIC_ATTITUDE_SLAVERY_CASTE_SYSTEM</Reason>
    </CivicAttitudeModifier>
    <CivicAttitudeModifier>
        <Civic>CIVIC_EMANCIPATION</Civic>
        <iModifier>-4</iModifier>
        <Reason>TXT_KEY_CIVIC_ATTITUDE_SLAVERY_EMANCIPATION</Reason>
    </CivicAttitudeModifier>
</CivicAttitudeModifiers>
This shouldn't be that much more difficult to implement, and I like the extra flexibility. Instead of specifying the <Civic> for each <CivicAttitudeModifier>, it may be easier to force the XML to have one <CivicAttitudeModifier> for each civic in that option type (5 in the normal game).

In any case, you'd probably want to code it as two arrays each with <num-civics-per-option-type> elements, one to hold the modifiers, the other to hold the messages. This makes the code easier to write.

Okay,I like your ideas, and flexibility is always a plus, but I'm new to arrays. They aren't as easy to base off a preexisting one, because they differ so much. So basically, I have to make completely new code. (There doesn't happen to be any guides to Arrays, are there?) I'm not quite sure I understand how everything is going to be coded. For example, how do I code it so that I am referencing a TXT file in the CvInfos? And how do I code it so the modifier changes based on civics, because I don't want to hardcode XML into the SDK. Originally, I was planning on this code, for the Attitude modifier, but I can see it isn't going to be enough.
Code:
int CvPlayerAI::AI_getCivicAttitudeModifier(PlayerTypes ePlayer) const
{
    int iAttitude = 0;
    for (int iI = 0; iI < GC.getNumCivicOptionInfos(); iI++)
    {
        if (getCivics((CivicOptionTypes)iI) == GET_PLAYER(ePlayer).getCivics((CivicOptionTypes)iI))
        {
            iAttitude = 0 //If both players have the same civic, who cares?
        } 
        
        if (getCivics((CivicOptionTypes)iI) != GET_PLAYER(ePlayer).getCivics((CivicOptionTypes)iI))
        {
            iAttitude += GC.getCivicInfo((CivicTypes)getCivics((CivicOptionTypes)iI)).getCivicAttitudeModifier();
        }
    }
    return iAttitude;
}

What else would I have to add so it changes based on the different civics?
 
There are a few array examples you can start with. CvCivicInfo has several itself; let's go with m_paiBuildingHappinessChanges because it's closest I think: number of slots in the array defined by XML rather than code and it has the same type (int).

Let's do the civic modifier value first since it's the same type as the example. I'm also going to call it "CivicAttitudeChanges" because Modifier in code tends to mean a percentage whereas Change means an added value.

You declare it easily enough in the header file by adding a * after its type (int):

Code:
// Arrays
...
int** m_ppiImprovementYieldChanges;

[B]int* m_piCivicAttitudeChanges;[/B]

In the source (.ccp) file you need to initialize it in the constructor,

Code:
CvCivicInfo::CvCivicInfo() :
...
m_ppiImprovementYieldChanges(NULL)[B],[/B]    [I]// don't forget to add a comma[/I]
[B]m_piCivicAttitudeChanges(NULL)[/B]
{
}

delete it in the destructor,

Code:
CvCivicInfo::~CvCivicInfo()
{
    ...
    if (m_ppiImprovementYieldChanges != NULL)
    { ... }
    
    [B]SAFE_DELETE_ARRAY(m_piCivicAttitudeChanges);[/B]
}

provide access to its values (don't forget to declare this new function in the header file),

Code:
int CvCivicInfo::getCivicAttitudeChange(int i) const
{
	FAssertMsg(i < GC.getNumCivicInfos(), "Index out of bounds");
	FAssertMsg(i > -1, "Index out of bounds");
	return m_piCivicAttitudeChanges ? m_piCivicAttitudeChanges[i] : 0;
}

read/write it to a saved game (I leave this to you as it's exactly like example), and read them from XML. This last one is tricky because it will have the message text keys paired up with the civics and modifiers rather than number-of-civics values in a list.

Note that the way I've coded this allows for civic to have modifiers applied to civics in different option lists (legal versus government). For example, Free Religion could be angered by a Police State. To constrain the modifiers to within the same civic option actually makes the code harder, so I think this is the way to go. Nothing forces you to actually have any modifiers cross any option groupings, but the option is there for those that want it.

To access these modifiers in the game you need to take this cross-option grouping into account and check for a modifier for each civic combination the pair of players is running. Let's say player X is running A, B, C, D, and E while player Y is running 1, 2, 3, 4, and 5. You need to list the modifiers for A1, A2, A3, ... E3, E4, E5 (5 x 5 = 25 total possible modifiers.

Code:
int CvPlayerAI::AI_getCivicAttitudeModifier(PlayerTypes ePlayer) const
{
	int iAttitude = 0;
	
	// add modifier for every combination of civics between the two players
	for (int iI = 0; iI < GC.getNumCivicOptionInfos(); iI++)
	{
		for (int iJ = 0; iJ < GC.getNumCivicOptionInfos(); iJ++)
		{
			iAttitude += GC.getCivicInfo(getCivics((CivicOptionTypes)iI)).getCivicAttitudeChange(GET_PLAYER(ePlayer).getCivics((CivicOptionTypes)iJ));
		}
	}
	
	return iAttitude;
}

This code assumes that the modifiers in Civic X determine how a player running that civic feel about players running the other civics. I had originally thought it would be the other way around. You need to decide which you'd prefer in XML where users will deal with it:

  1. Slavery has a list of modifiers that affect other player's attitude toward a Slavery player, or
  2. Slavery stores a list of modifiers that affect a Slavery player's attitude toward other players.

If you prefer the first method in XML instead, use this line in the above code:

Code:
iAttitude += GC.getCivicInfo(GET_PLAYER(ePlayer).getCivics((CivicOptionTypes)iJ)).getCivicAttitudeChange(getCivics((CivicOptionTypes)iI));

You will use a similar nested loop in CvGameTextMgr to display all these individual attitude modifiers and their messages. Instead of adding to a single value, if the value isn't zero you'll add its message containing the value to the string buffer.

So now, reading from XML. :) This is the trickiest part and will have to come later. For now, you can set a few test values in the function that does the XML reading after creating the array.

Hmm, actually it's going to be tougher than I thought. The problem is that you cannot reference CIVIC_FOO strings while parsing the civics. You may have to first read all the values into temporary arrays and then post-process these arrays into the real arrays after reading all civics. Is there a function that gets called after reading in all civics or after reading all XML data?

What I mean is that while reading in the definitions for civics you cannot look up a civic by its key (that civic may not have been parsed yet). Instead you'll store the key itself ("CIVIC_SLAVERY") in the CvCivicInfo. Once all civics have been read in you can now look them up by key, so you call another function that loops over all civics and performs the lookup to build the actual arrays.

Homework for you: Find a function that gets called either a) after all civics have been processed or b) after all XML has been processed. It will be in the core XML loading routines outside of CvInfo.cpp. I recall seeing functions with SecondPass in the name. These are good starts as this is what we will need to do, a second pass over the data you just read to process it further.

xienwolf can probably tell you straight away where to look or the actual function itself.
 
Thanks for all the help, I already had the array set up almost right in CvInfos. The Attitude change formula, will it not change the attitude if the two nations have the same civic? (If both nations have slavery, who cares?)

Homework for you: Find a function that gets called either a) after all civics have been processed or b) after all XML has been processed. It will be in the core XML loading routines outside of CvInfo.cpp. I recall seeing functions with SecondPass in the name. These are good starts as this is what we will need to do, a second pass over the data you just read to process it further.

You mean Readpass2? A few functions in CvInfos have them, but not Civics, we will have to create it ourselves. I'm basing this off of others, but tell me if this looks right.

Code:
bool CvCivicInfo::readPass2(CvXMLLoadUtility* pXML)
{
    CvString szTextVal;
/*************************************************************************************************/
/** XMLCOPY                                 10/14/07                                MRGENIE      */
/**                                                                                              */
/**                                                                                              */
/*************************************************************************************************/
    if (!CvInfoBase::read(pXML))
    {
        return false;
    }
/*************************************************************************************************/
/** XMLCOPY                                 END                                                  */
/*************************************************************************************************/

    pXML->GetChildXmlValByName(szTextVal, "CivicAttitudeChange");
    m_piCivicAttitudeChange = GC.getInfoTypeForString(szTextVal);

    return true;
}

and
Code:
void CvCivicInfo::copyNonDefaults(CvCivicInfo* pClassInfo)
{...
    if (m_piCivicAttitudeChange == iTextDefault) m_piCivicAttitudeChange = pClassInfo->getCivicAttitudeChange();
}


void CvCivicInfo::copyNonDefaultsReadPass2(CvCivicInfo* pClassInfo)
{    
    int iTextDefault = -1;  //all integers which are TEXT_KEYS in the xml are -1 by default
    
    if (m_piCivicAttitudeChange == iTextDefault) m_piCivicAttitudeChange = pClassInfo->getCivicAttitudeChange();
}
 
Someone may want Slavery players to feel good about each other by adding a Slaver-Slavery modifier.

+1: We applaud your whip-mastery!​

It's easier to allow it than to block it, but we could block if it you really want. Just check if the two civics are the same before adding the value to iAttitude.

As for readPass2(), that looks like what we want. We'll need to do all of the modifier processing in readPass2() instead of read(). Let me look at an example in the code. Here's psuedocode:

Code:
Locate <CivicAttitudeChanges> element
For each <CivicAttutdeChange> child element, do
    Grab and lookup <Civic> to get CivicTypes value
    Store <AttitudeChange> value in array
    Store <Text> value in array

I recall someone saying that when other CvInfo classes store a text key, they also cache the looked up value. See if you can spot any infos that do that for an example.
 
Someone may want Slavery players to feel good about each other by adding a Slaver-Slavery modifier.
+1: We applaud your whip-mastery!​
It's easier to allow it than to block it, but we could block if it you really want. Just check if the two civics are the same before adding the value to iAttitude.

No, No. You're right, I just hadn't thought of that.
As for readPass2(), that looks like what we want. We'll need to do all of the modifier processing in readPass2() instead of read(). Let me look at an example in the code. Here's psuedocode:

Code:
Locate <CivicAttitudeChanges> element
For each <CivicAttutdeChange> child element, do
    Grab and lookup <Civic> to get CivicTypes value
    Store <AttitudeChange> value in array
    Store <Text> value in array
I recall someone saying that when other CvInfo classes store a text key, they also cache the looked up value. See if you can spot any infos that do that for an example.
I based the code for readpass2 off of improvements, because when the grow or get pillaged, the game needs to know which improvement to grow to or which to revert to. So, the code you're thinking of might be this...

Code:
void CvImprovementInfo::copyNonDefaultsReadPass2(CvImprovementInfo* pClassInfo)
{    
    int iTextDefault = -1;  //all integers which are TEXT_KEYS in the xml are -1 by default
    
    if (m_iImprovementPillage == iTextDefault) m_iImprovementPillage = pClassInfo->getImprovementPillage();
    if (m_iImprovementUpgrade == iTextDefault) m_iImprovementUpgrade = pClassInfo->getImprovementUpgrade();
}

But I'm not really sure. I'm venturing into uncharted waters here.
 
Strange, my SDK has no CvImprovementInfo::copyNonDefaultsReadPass2(). Is that located elsewhere? However, looking at CvImprovementInfo::readpass2(), this is indeed what we need. It looks like you can perform the same operations that read() does as well as look up other civics by key.

I'm eating dinner right now, so I'll finish and post the code in a little while.
 
Strange, my SDK has no CvImprovementInfo::copyNonDefaultsReadPass2(). Is that located elsewhere? However, looking at CvImprovementInfo::readpass2(), this is indeed what we need. It looks like you can perform the same operations that read() does as well as look up other civics by key.

I'm eating dinner right now, so I'll finish and post the code in a little while.

It was added in WoC. I use RevDCM's SDK as my base.
 
Give this a shot. Note that I haven't tried to compile it. I patterned it after the code to read the BonusTypeStructs for CvImprovementInfo.

Code:
bool CvCivicInfo::readPass2(CvXMLLoadUtility* pXML)
{
	if (gDLL->getXMLIFace()->SetToChildByTagName(pXML->GetXML(),"CivicAttitudeChanges"))
	{
		if (SkipToNextVal())
		{
			// create arrays to hold attitude changes and messages
			SAFE_DELETE_ARRAY(m_piCivicAttitudeChanges);
			m_piCivicAttitudeChanges = new int[GC.getNumCivicInfos()];
			//SAFE_DELETE_ARRAY(m_pszCivicAttitudeTextKeys);
			//m_pszCivicAttitudeTextKeys = new CvString[GC.getNumCivicInfos()];
			
			for (int i = 0; i < GC.getNumCivicInfos(); i++)
			{
				m_piCivicAttitudeChanges[i] = 0;
				//m_pszCivicAttitudeTextKeys[i] = NULL;
			}
			
			int iCount = gDLL->getXMLIFace()->GetNumChildren(m_pFXml);
			TCHAR szCivicKey[256];
			
			if (gDLL->getXMLIFace()->SetToChild(m_pFXml))
			{
				if(!(iCount <= GC.getNumCivicInfos()))
				{
					char	szMessage[1024];
					sprintf( szMessage, "For loop iterator is greater than array size \n Current XML file is: %s", GC.getCurrentXMLFile().GetCString());
					gDLL->MessageBox(szMessage, "XML Error");
				}
				
				for (int i = 0; i < iCount; i++)
				{
					if (SkipToNextVal())
					{
						if (GetChildXmlVal(szCivicKey))
						{
							int iCivicType = FindInInfoClass(szCivicKey);
							if (iCivicType >= 0)
							{
								GetNextXmlVal(&m_piCivicAttitudeChanges[iCivicType]);
								//GetNextXmlVal(&m_pszCivicAttitudeTextKeys[iCivicType]);
							}
							else
							{
								// skip unknown civic type
								gDLL->getXMLIFace()->SetToParent(m_pFXml);
							}
						}
						
						if (!gDLL->getXMLIFace()->NextSibling(m_pFXml))
						{
							break;
						}
					}
				}
				
				gDLL->getXMLIFace()->SetToParent(pXML->GetXML());
			}
		}
		
		gDLL->getXMLIFace()->SetToParent(pXML->GetXML());
	}
}
 
Okay, well, I'm compiling. There is no way in hell this will version will even compile, but at least the errors will point us in the right direction.
 
Don't forget you need to declare this new function in the header file (CvInfos.h) under CvCivicInfo. Look for where it's declared in CvImprovementInfo and put it in the same relative place within the CvCivicInfo class.
 
Don't forget you need to declare this new function in the header file (CvInfos.h) under CvCivicInfo. Look for where it's declared in CvImprovementInfo and put it in the same relative place within the CvCivicInfo class.

Thanks for the reminder. I had already added that, but never included the
Code:
 int* m_piCivicAttitudeChanges;
in the array section, leading to lots of errors.
 
I'm getting some errors in CvGlobals, after I added some code there.

My Errors are

CvGlobals.cpp(2416) : error C3861: 'm_piCivicAttitudeChanges': identifier not found, even with argument-dependent lookup

and my code in CvGlobals.cpp is
Code:
int CvGlobals::getNumCivicAttitudeChanges()//Afforess
{
	return (int)m_piCivicAttitudeChanges();
}

CvCivicInfo& CvGlobals::getCivicAttitudeChanges(CivicTypes eCivicNum) //Afforess
{
	return *(m_piCivicAttitudeChanges);
}

and in the Globals.h it is
Code:
	int getNumCivicAttitudeChanges();
	std::vector<CvCivicInfo*>& getCivicAttitudeChanges();
	CvCivicInfo& getCivicAttitudeChanges(CivicTypes eCivicNum);
 
I don't see a need for these extra functions in CvGlobals.cpp. The modifiers are all available by first looking up the appropriate CvCivicInfo. Can you describe a use case where you'd need this information without a CvCivicInfo?
 
I'm not sure how to write
Code:
	SAFE_DELETE_ARRAY(m_piCivicAttitudeChanges);
	m_piCivicAttitudeChanges = new int[GC.getNumCivicAttitudeChanges()];
	stream->Read(GC.getNumCivicAttitudeChanges(), m_piCivicAttitudeChanges);

without them.
 
The arrays will have number-of-civic-types slots:

Code:
SAFE_DELETE_ARRAY(m_piCivicAttitudeChanges);
m_piCivicAttitudeChanges = new int[GC.[B]getNumCivicInfos[/B]()];
stream->Read(GC.[B]getNumCivicInfos[/B](), m_piCivicAttitudeChanges);

Note also that I added two calls to SAFE_DELETE_ARRAY to readpass2() above (one commented out until we add the message key handling.
 
The arrays will have number-of-civic-types slots:

Code:
SAFE_DELETE_ARRAY(m_piCivicAttitudeChanges);
m_piCivicAttitudeChanges = new int[GC.[B]getNumCivicInfos[/B]()];
stream->Read(GC.[B]getNumCivicInfos[/B](), m_piCivicAttitudeChanges);
Note also that I added two calls to SAFE_DELETE_ARRAY to readpass2() above (one commented out until we add the message key handling.

Yeah, I did notice the comments. Okay, so I got a bit farther compiling this time, but now I get a plethora of errors about "m_pFXml," "GetChildXmlVal" and "SkipToNextVal." (Can not find Identifier errors) and I get a wierd error about this line,

Code:
    pXML->SetVariableListTagPair(&m_piCivicAttitudeChanges, "CivicAttitudeChanges", GC.getCivicInfo(), GC.getNumCivicInfos());

Here is the error message:
error C2664: 'void CvXMLLoadUtility::SetVariableListTagPair(int ** ,const TCHAR *,int,int,int)' : cannot convert parameter 3 from 'std::vector<_Ty>' to 'int'
with
[
_Ty=CvCivicInfo *
]
No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
 
Try this code.

Code:
bool CvCivicInfo::readPass2(CvXMLLoadUtility* pXML)
{
	if (gDLL->getXMLIFace()->SetToChildByTagName(pXML->GetXML(),"CivicAttitudeChanges"))
	{
		if (gDLL->getXMLIFace()->SkipToNextVal())
		{
			// create arrays to hold attitude changes and messages
			SAFE_DELETE_ARRAY(m_piCivicAttitudeChanges);
			m_piCivicAttitudeChanges = new int[GC.getNumCivicInfos()];
			//SAFE_DELETE_ARRAY(m_pszCivicAttitudeTextKeys);
			//m_pszCivicAttitudeTextKeys = new CvString[GC.getNumCivicInfos()];
			
			for (int i = 0; i < GC.getNumCivicInfos(); i++)
			{
				m_piCivicAttitudeChanges[i] = 0;
				//m_pszCivicAttitudeTextKeys[i] = NULL;
			}
			
			int iCount = gDLL->getXMLIFace()->GetNumChildren(pXML->GetXML());
			TCHAR szCivicKey[256];
			
			if (gDLL->getXMLIFace()->SetToChild(pXML->GetXML()))
			{
				if(!(iCount <= GC.getNumCivicInfos()))
				{
					char	szMessage[1024];
					sprintf( szMessage, "For loop iterator is greater than array size \n Current XML file is: %s", GC.getCurrentXMLFile().GetCString());
					gDLL->MessageBox(szMessage, "XML Error");
				}
				
				for (int i = 0; i < iCount; i++)
				{
					if (gDLL->getXMLIFace()->SkipToNextVal())
					{
						if (pXML->GetChildXmlVal(szCivicKey))
						{
							int iCivicType = FindInInfoClass(szCivicKey);
							if (iCivicType >= 0)
							{
								pXML->GetNextXmlVal(&m_piCivicAttitudeChanges[iCivicType]);
								//pXML->GetNextXmlVal(&m_pszCivicAttitudeTextKeys[iCivicType]);
							}
							else
							{
								// skip unknown civic type
								gDLL->getXMLIFace()->SetToParent(pXML->GetXML());
							}
						}
						
						if (!gDLL->getXMLIFace()->NextSibling(pXML->GetXML()))
						{
							break;
						}
					}
				}
				
				gDLL->getXMLIFace()->SetToParent(pXML->GetXML());
			}
		}
		
		gDLL->getXMLIFace()->SetToParent(pXML->GetXML());
	}
}

As for that last error, where is that line from? Can you give me some context around it? It wasn't in the code I posted.
 
I still get errors regarding to "SkipToNextVal."

error C2039: 'SkipToNextVal' : is not a member of 'CvDLLXmlIFaceBase'

That code was part of
Code:
bool CvCivicInfo::read(CvXMLLoadUtility* pXML)
But now, I realize that it (probably) isn't necessary because we need to read after the second pass.
 
Top Bottom