Civic Attitude Modifier

Ah, I forgot that you must add one line to the CvCivicInfos section:

Code:
<ElementType name="CivicInfo" content="eltOnly">
    ...
    [B]<element type="CivicAttitudeChanges" minOccurs="0" maxOccurs="*"/>[/B]
    ...
</ElementType>
 
I still get the same errors. My schema looks like this.
Code:
    <!-- Afforess -->
    <ElementType name="CivicType" content="textOnly"/>
    <ElementType name="iAttitudeChange" content="textOnly" dt:type="int"/>
    <ElementType name="CivicAttitudeChange" content="eltOnly">
        <element type="CivicType"/>
        <element type="iAttitudeChange"/>
        <!--element type="Description"-->
    </ElementType>
    <ElementType name="CivicAttitudeChanges" content="eltOnly">
        <element type="CivicAttitudeChange" maxOccurs="*"/>
        <element type="CivicAttitudeChanges" minOccurs="0" maxOccurs="*"/>
    </ElementType>
    <!-- Afforess -->
 
No, this line

Code:
<element type="CivicAttitudeChanges" minOccurs="0" maxOccurs="*"/>

goes in the block for CivicInfo's ElementType. Remove this line from what you just posted and put it where I showed in my previous post. I know they look very similar, but you need to search for

Code:
<ElementType name="[B]CivicInfo[/B]" content="eltOnly">

Also, I was asking to see the XML for one of the civics from CIV4CivicInfos.xml that uses this new feature, but I'm glad you posted your schema again.
 
Hmm. Still doesn't work.

I have this section of code.
Code:
<!-- Afforess -->
	<ElementType name="CivicType" content="textOnly"/>
	<ElementType name="iAttitudeChange" content="textOnly" dt:type="int"/>
	<ElementType name="CivicAttitudeChange" content="eltOnly">
		<element type="CivicType"/>
		<element type="iAttitudeChange"/>
		<!--element type="Description"-->
	</ElementType>
	<ElementType name="CivicAttitudeChanges" content="eltOnly">
		<element type="CivicAttitudeChange" maxOccurs="*"/>
	</ElementType>
<!-- Afforess -->

and this section
Code:
	<ElementType name="CivicInfo" content="eltOnly">
		...
		<element type="CivicAttitudeChanges" minOccurs="0" maxOccurs="*"/>
	</ElementType>
 
Okay, looks like there are already definitions for <CivicType> and <iAttitudeChange>, so remove these two lines. If that doesn't work, remove the commented-out line for Description

Code:
<!-- Afforess -->
	[B][s]<ElementType name="CivicType" content="textOnly"/>[/s][/B]
	[B][s]<ElementType name="iAttitudeChange" content="textOnly" dt:type="int"/>[/s][/B]
	<ElementType name="CivicAttitudeChange" content="eltOnly">
		<element type="CivicType"/>
		<element type="iAttitudeChange"/>
		[B]<!--element type="Description"-->[/B]
	</ElementType>
	<ElementType name="CivicAttitudeChanges" content="eltOnly">
		<element type="CivicAttitudeChange" maxOccurs="*"/>
	</ElementType>
<!-- Afforess -->
 
Okay, with that change, I get no LoadXML errors, but as soon as I get to "init XML" Windows tells me that the game has had an error, and must close. My logs don't show anything strange occurring. No errors or failed attempts at loading XML.
 
Great! Seriously, one major hurdle is now behind us. Can you post your latest CvInfos.cpp please? I suspect we're missing the allocation of one of the arrays before using it. Oh, if you have it, could you also post the file from which you started so I can do a diff and more easily see what has been added?
 
Well, here's the source of the int/bool warning:

Code:
if ( m_piCivicAttitudeChanges[i] == [B]bDefault[/B] )

should be

Code:
if ( m_piCivicAttitudeChanges[i] == [B]iDefault[/B] )

But this isn't the cause of the crash since "false" gets cast to "0" which is what we want. I first suspected that the array isn't created before copyNonDefaults() gets called, but I see that none of the other arrays are allocated in copyNonDefaults(). They must be created elsewhere. I'll poke around some more.
 
Ah, I think I found the problem. When a civic doesn't have any attitude modifiers, the array(s) aren't getting allocated. Move the array allocation to the top of the function:

Code:
bool CvCivicInfo::readPass2(CvXMLLoadUtility* pXML)
{
[B]	// 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()];[/B]

	if (gDLL->getXMLIFace()->SetToChildByTagName(pXML->GetXML(),"CivicAttitudeChanges"))
	{
		if (pXML->SkipToNextVal())
		{
			[s]// create arrays to hold attitude changes and messages[/s]
			[s]SAFE_DELETE_ARRAY(m_piCivicAttitudeChanges);[/s]
			[s]m_piCivicAttitudeChanges = new int[GC.getNumCivicInfos()];[/s]
			[s]//SAFE_DELETE_ARRAY(m_pszCivicAttitudeTextKeys);[/s]
			[s]//m_pszCivicAttitudeTextKeys = new CvString[GC.getNumCivicInfos()];[/s]

			for (int i = 0; i < GC.getNumCivicInfos(); i++)
			...
 
Okay, I made all those changes, and it compiled fine, but I still get a CTD at Init XML.
 
What happens if you comment out the entire if() block in readpass2()?

Code:
bool CvCivicInfo::readPass2(CvXMLLoadUtility* pXML)
{
	// 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()];

/*	if (gDLL->getXMLIFace()->SetToChildByTagName(pXML->GetXML(),"CivicAttitudeChanges"))
	{
		...
	}
*/	
	return true;
}
 
Again, it compiles fine, but I get a CTD at Init XML. Just to make sure it wasn't a foreign problem causing the CTD, I threw in my older DLL and the game loaded up fine.
 
In your CvInfos.cpp I don't see

Code:
return true;

at the end of CvCivicInfo::readPass2(). It must be the very last line before the closing brace. I would expect the compiler to fail, but perhaps it's returning false (default value for bool) which tells the XML routine to fail.

Code:
bool CvCivicInfo::readPass2(CvXMLLoadUtility* pXML)
{
	// create arrays to hold attitude changes and messages
	...
	}
	
	[B]return true;[/B]
}

Also, let's be safe and change copyNonDefaults() so that it checks for the array allocation before trying to copy:

Code:
[B]	if (m_piCivicAttitudeChanges)
	{[/B]
		for ( int i = 0; i < GC.getNumCivicInfos(); i++ )
		{
			if ( m_piCivicAttitudeChanges[i] == bDefault )
			{
				m_piCivicAttitudeChanges[i] = pClassInfo->getCivicAttitudeChanges(i);
			}
		}
[B]	}[/B]
 
In your CvInfos.cpp I don't see

Code:
return true;
at the end of CvCivicInfo::readPass2(). It must be the very last line before the closing brace. I would expect the compiler to fail, but perhaps it's returning false (default value for bool) which tells the XML routine to fail.

I have two copies of the SDK that I am working with. The copy that I regularly edit, and the copy in the Compiler folder. I accidently gave you a slightly out of date copy. I fixed that code in the compiler version, but never went back and updated my other folder.
Code:
bool CvCivicInfo::readPass2(CvXMLLoadUtility* pXML)
{
    // create arrays to hold attitude changes and messages
    ...
    }
    
    [B]return true;[/B]
}
Also, let's be safe and change copyNonDefaults() so that it checks for the array allocation before trying to copy:

Code:
[B]    if (m_piCivicAttitudeChanges)
    {[/B]
        for ( int i = 0; i < GC.getNumCivicInfos(); i++ )
        {
            if ( m_piCivicAttitudeChanges[i] == bDefault )
            {
                m_piCivicAttitudeChanges[i] = pClassInfo->getCivicAttitudeChanges(i);
            }
        }
[B]    }[/B]

Okay, I made those changes and compiled successfully. Now, good news and bad news. The game loads up, but I can't see any changes to the civics in the civilopedia or in a test game I ran.
 
That last piece tells me that the array holding the values is never initialized before copyNonDefaults() is called. That last bit of safety code ensures that we don't write to an unallocated array, but it also means the values are never copied.

You need to look at another CvFooInfo class that a) has a copyNonDefaults() function, b) a readpass2() function, and c) an array that gets read into in readpass2(). Oh, is there a copyNonDefaults2() function that goes with readpass2()? How does this all work, do you know? Where's the code that actually calls readpass2() and copyNonDefaults() when reading?
 
That last piece tells me that the array holding the values is never initialized before copyNonDefaults() is called. That last bit of safety code ensures that we don't write to an unallocated array, but it also means the values are never copied.

You need to look at another CvFooInfo class that a) has a copyNonDefaults() function, b) a readpass2() function, and c) an array that gets read into in readpass2(). Oh, is there a copyNonDefaults2() function that goes with readpass2()? How does this all work, do you know? Where's the code that actually calls readpass2() and copyNonDefaults() when reading?

I'll try to answer your questions in reverse order. Th PromotionInfos seem to be a good place to show off readpass2. There is everything you want.

Here's code calling up readpass2 stuff,
Code:
void CvPromotionInfo::copyNonDefaults(CvPromotionInfo* pClassInfo)
{    ...
    if (getPrereqPromotion() == iTextDefault) m_iPrereqPromotion = pClassInfo->getPrereqPromotion();
    if (getPrereqOrPromotion1() == iTextDefault) m_iPrereqOrPromotion1 = pClassInfo->getPrereqOrPromotion1();
    if (getPrereqOrPromotion2() == iTextDefault) m_iPrereqOrPromotion2 = pClassInfo->getPrereqOrPromotion2();
...}

There is a Noncopydefaults2

Code:
void CvPromotionInfo::copyNonDefaultsReadPass2(CvPromotionInfo* pClassInfo)
{    
    int iTextDefault = -1;  //all integers which are TEXT_KEYS in the xml are -1 by default
    
    if (getPrereqPromotion() == iTextDefault) m_iPrereqPromotion = pClassInfo->getPrereqPromotion();
    if (getPrereqOrPromotion1() == iTextDefault) m_iPrereqOrPromotion1 = pClassInfo->getPrereqOrPromotion1();
    if (getPrereqOrPromotion2() == iTextDefault) m_iPrereqOrPromotion2 = pClassInfo->getPrereqOrPromotion2();
}

So we might want to look at that. I believe a lot of the pass2 code was added with WoC, so only mods with WoC in them will have them. (Like RevDCM). We might consider asking Xienwolf for his help as well, as he seems to be much more familiar with readpass2 code than me or you.
 
Perhaps you need to move the code you added to copyNonDefaults() to copyNonDefaultsReadpass2()--create a new function:

Code:
void CvCivicInfo::copyNonDefaultsReadPass2(CvCivicInfo* pClassInfo)
{
	if (m_piCivicAttitudeChanges)
	{
		for ( int i = 0; i < GC.getNumCivicInfos(); i++ )
		{
			if ( m_piCivicAttitudeChanges[i] == bDefault )
			{
				m_piCivicAttitudeChanges[i] = pClassInfo->getCivicAttitudeChanges(i);
			}
		}
	}
}

I don't know why you need to copy values for the first time you read in the XML, but perhaps that's just part of WoC.

Next, looking at CvTechInfo::read() I see this comment:

Need to create these arrays here for the CopyNonDefaults Comparison​

I think you need to allocate the arrays in read() and then fill them in readpass2() and copyNonDefaultsReadPass2(). Move this code from readpass2() to the end of read():

Code:
	// 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()];

BTW, how are you writing the XML for your civics? Are you putting just your additions into a module XML file or modifying a full copy of CIV4CivicInfo.xml?
 
Okay, I made those changes, along with a few other tweaks that you forgot (like
Code:
 	int iDefault = 0;
in the Copynondefaultspass2.) I got it finally to compile, but it still does nothing in game. I'm starting to think the formulas in CvPlayerAI.cpp are not right. Also, should there be anything in CvPlayer.cpp? So far I haven't changed it.

The XML is in the main CivicInfos XML file. Why, do you prefer modules?
 
I was just asking if you used modules or not; I have no preference. Have you tried checking the values from the in-game console? Start up a game and hit ~ (shift `) to get the Python console. Then type this in:

Code:
c = gc.getCivicInfo(gc.getInfoTypeForString("[B]CIVIC_SLAVERY[/B]"))
print c.getCivicAttitudeChanges(gc.getInfoTypeForString("[B]CIVIC_EMANCIPATION[/B]"))

Test out some of the civics that you set attitude changes for.
 
Back
Top Bottom