Civic Attitude Modifier

I'm not sure what values the power can take on, but you might want to go with a power ratio rather than the difference. Power ratios tend to vary between 0.5 and 2.0, so one is typically not more than twice another unless a player is dying or severely backwards. And it's the ratio that should matter--not the difference. As two powerful civs gain in power, their ratio will stay the same but their difference will increase. Given how powerful they are, the effective fighting difference is really the same even though its scale increases.

What I mean is this, for example one player has 10 spearmen and the other 20. The difference is 10 spearmen and the ratio is 2:1. Now they upgrade over the centuries so one has 10 tanks and the other 20. The difference is 10 tanks. 10 tanks is much stronger than 10 spearmen, but the ratio remains 2:1. The first player shouldn't fear the second player any more when they both had spearmen. So the factor for changing civics shouldn't change.
 
I'm not sure what values the power can take on, but you might want to go with a power ratio rather than the difference. Power ratios tend to vary between 0.5 and 2.0, so one is typically not more than twice another unless a player is dying or severely backwards. And it's the ratio that should matter--not the difference. As two powerful civs gain in power, their ratio will stay the same but their difference will increase. Given how powerful they are, the effective fighting difference is really the same even though its scale increases.

What I mean is this, for example one player has 10 spearmen and the other 20. The difference is 10 spearmen and the ratio is 2:1. Now they upgrade over the centuries so one has 10 tanks and the other 20. The difference is 10 tanks. 10 tanks is much stronger than 10 spearmen, but the ratio remains 2:1. The first player shouldn't fear the second player any more when they both had spearmen. So the factor for changing civics shouldn't change.

Excellent Point. I can't believe I forgot about that. Here's the updated version:
Code:
    iTempValue = 0;
    int iPlayerValue = 0;
    CivicTypes eTargetCivic;
    for (iI = 0; iI < MAX_PLAYERS; iI++)
    {
        int iOurPower = std::max(1, GET_TEAM(getTeam()).getPower(true));
        int iTheirPower = std::max(1, GET_TEAM(GET_PLAYER((PlayerTypes)iI).getTeam()).getDefensivePower());
        
        if (!GET_PLAYER((PlayerTypes)iI).isHuman() && GET_PLAYER((PlayerTypes)iI).isAlive() && (GET_PLAYER((PlayerTypes)iI).getTeam() != getTeam()) && (!GET_PLAYER((PlayerTypes)iI).isBarbarian()))
        {
            for (int iJ = 0; iJ < GC.getNumCivicOptionInfos(); iJ++)
            {
                eTargetCivic = GET_PLAYER((PlayerTypes)iI).getCivics((CivicOptionTypes)iJ);
                int iAttitudeChange = GC.getCivicInfo(eCivic).getCivicAttitudeChange(eTargetCivic);
                int iCurrentAttitude = AI_getAttitudeVal((PlayerTypes)iJ);
                //We are close friends
                if (iCurrentAttitude > 5)
                {//Positive Changes are welcome, negative ones, not so much
                    iPlayerValue += iAttitudeChange * 3;
                }
                //we aren't friends
                else
                {//if we aren't gearing up for a war yet...
                    if (GET_TEAM(getTeam()).AI_getWarPlan((TeamTypes)iI) != NO_WARPLAN)
                    {//Then we would welcome some diplomatic improvements
                        iPlayerValue += iAttitudeChange * 3;
                        iPlayerValue /= 2;
                    }
                    else
                    {
                        //We are going to war, screw diplomacy
                    }
                }
            }
            if (GET_TEAM(getTeam()).isVassal(GET_PLAYER((PlayerTypes)iI).getTeam()))
            {//Who cares about vassals?
                iPlayerValue /= 5;
            }
            float fPowerRatio = (float)((iTheirPower)/iOurPower);
            iPlayerValue = (int)((float)iPlayerValue * fPowerRatio);
        }
        iTempValue += iPlayerValue;
        iPlayerValue = 0;
    }
    iValue += iTempValue;
 
Now, the fun isn't over yet. I've been making all these changes in a "clean" environment, so to speak, in it's own little mod. It has no other changes to base BTS than this. Now that I felt that I was ready to port it over to my heavily modified copy of RevDCM's source code, I get some errors...

I added all the code, enabled the readpass2, and then, because I am using WoC, I added a copyNonDefaultsReadPass2(...) to my CvInfos as well... I tried to mimic the style of other copyNonDefaultsReadPass2's, but I got an instant CTD when Init XML. Here's the code:

Code:
void CvCivicInfo::copyNonDefaultsReadPass2(CvCivicInfo* pClassInfo)
{
	bool bDefault = false;
	int iDefault = 0;
	int iTextDefault = -1;  //all integers which are TEXT_KEYS in the xml are -1 by default
	int iAudioDefault = -1;  //all audio is default -1	
	float fDefault = 0.0f;
	CvString cDefault = CvString::format("").GetCString();
	CvWString wDefault = CvWString::format(L"").GetCString();

	if ( m_piCivicAttitudeChanges != NULL)
	{
		FAssert(false);
	}

	if(m_piCivicAttitudeChanges == NULL)
	{
		m_piCivicAttitudeChanges = new int[GC.getNumCivicInfos()];
		for ( int i = 0; i < GC.getNumCivicInfos(); i++)
			m_piCivicAttitudeChanges[i] = 0;
	}
	for ( int i = 0; i < GC.getNumCivicInfos(); i++)
	{
		if (pClassInfo->getCivicAttitudeChange(i) != 0)
			m_piCivicAttitudeChanges[i] = pClassInfo->getCivicAttitudeChange(i);
	}
	
	if ( m_pszCivicAttitudeReason != NULL)
	{
		FAssert(false);
	}

	if(m_pszCivicAttitudeReason == NULL)
	{
		m_pszCivicAttitudeReason = new CvString[GC.getNumCivicInfos()];
		for ( int i = 0; i < GC.getNumCivicInfos(); i++)
			m_pszCivicAttitudeReason[i] = 0;
	}
	for ( int i = 0; i < GC.getNumCivicInfos(); i++)
	{
		if (pClassInfo->getCivicAttitudeReason(i) != 0)
			m_pszCivicAttitudeReason[i] = pClassInfo->getCivicAttitudeReason(i);
	}
}

I'm no WoC expert, so I just commented out the inside of the function, recompiled, and I could load. If only the errors stopped there...

Now, I went straight to the main menu Civilopedia and looked at the civics section. It was completely destroyed:
Civ4ScreenShot0134.JPG

Following your advice, I compiled a debug dll, and attached it to VS, and looked at the code. It seems like every modularly-added civic (I added quite a few civics to RoM in modules) civicAttitudeChange is holding a garbage value. Also, I gave Chiefdom have some real modifiers, but those were all ignored, replaced with 0's. (However, this may be because I did not add the copyNonDefaultsReadPass2(...) code yet.

Before you ask, yes I double checked that I merged all the code over, and correctly, and yes, I did switch the "false" to a "true" for LoadGlobalClassInfo(CivicInfo....) so it had a readpass2.

Any ideas what's causing the problems?
 
The code in CNDr2 needs to be in CND as well as I recall, pretty much completely unchanged. The standard issue with what you are seeing is that you are not initializing arrays at the right point, especially if they don't already exist (ie - nothing was actually loaded) and you don't make a new array at all.
 
The code in CNDr2 needs to be in CND as well as I recall, pretty much completely unchanged. The standard issue with what you are seeing is that you are not initializing arrays at the right point, especially if they don't already exist (ie - nothing was actually loaded) and you don't make a new array at all.

Hmm, that was a problem I had, I did forget the code in CND. However, adding it and re-enabling the code in CNDr2 still caused a CTD when init XML. Here's the code in CND:

Code:
    if(m_pszCivicAttitudeReason == NULL)
    {
        m_pszCivicAttitudeReason = new CvString[GC.getNumCivicInfos()];
        for ( int i = 0; i < GC.getNumCivicInfos(); i++)
            m_pszCivicAttitudeReason[i] = 0;
    }
    for ( int i = 0; i < GC.getNumCivicInfos(); i++)
    {
        if (pClassInfo->getCivicAttitudeReason(i) != 0)
            m_pszCivicAttitudeReason[i] = pClassInfo->getCivicAttitudeReason(i);
    }

Here's my CNDr2:

Code:
void CvCivicInfo::copyNonDefaultsReadPass2(CvCivicInfo* pClassInfo)
{
    bool bDefault = false;
    int iDefault = 0;
    int iTextDefault = -1;  //all integers which are TEXT_KEYS in the xml are -1 by default
    int iAudioDefault = -1;  //all audio is default -1    
    float fDefault = 0.0f;
    CvString cDefault = CvString::format("").GetCString();
    CvWString wDefault = CvWString::format(L"").GetCString();

    if ( m_piCivicAttitudeChanges != NULL)
    {
        FAssert(false);
    }

    if(m_piCivicAttitudeChanges == NULL)
    {
        m_piCivicAttitudeChanges = new int[GC.getNumCivicInfos()];
        for ( int i = 0; i < GC.getNumCivicInfos(); i++)
            m_piCivicAttitudeChanges[i] = 0;
    }
    for ( int i = 0; i < GC.getNumCivicInfos(); i++)
    {
        if (pClassInfo->getCivicAttitudeChange(i) != 0)
            m_piCivicAttitudeChanges[i] = pClassInfo->getCivicAttitudeChange(i);
    }
    
    if ( m_pszCivicAttitudeReason != NULL)
    {
        FAssert(false);
    }

    if(m_pszCivicAttitudeReason == NULL)
    {
        m_pszCivicAttitudeReason = new CvString[GC.getNumCivicInfos()];
        for ( int i = 0; i < GC.getNumCivicInfos(); i++)
            m_pszCivicAttitudeReason[i] = 0;
    }
    for ( int i = 0; i < GC.getNumCivicInfos(); i++)
    {
        if (pClassInfo->getCivicAttitudeReason(i) != 0)
            m_pszCivicAttitudeReason[i] = pClassInfo->getCivicAttitudeReason(i);
    }
}
 
It should be set up so that it is absolutely impossible for the arrays to be NULL going in to CNDr2, so the checks for that ought not to be required, leaving the Asserts in place is a good choice though (however I would encase it all in the assert so you don't have extra lines of code in a non-debug compile)

Here is a sample of an array I have set up in CNDr2:

parts in CND:
Code:
	// Readpass2 stuff
	for ( int j = 0; j < GC.getNUM_OR_TECH_PREREQS(); j++)
	{
		if(getPrereqOrTechs(j)			== NO_TECH)			m_piPrereqOrTechs[j]					= pClassInfo->getPrereqOrTechs(j);
	}
	for ( int j = 0; j < GC.getNUM_AND_TECH_PREREQS(); j++)
	{
		if(getPrereqAndTechs(j)			== NO_TECH)			m_piPrereqAndTechs[j]					= pClassInfo->getPrereqAndTechs(j);
	}
	if(!m_piTechCostShift)									pXML->InitList(&m_piTechCostShift, GC.getNumTechInfos(), 0);
	if(!m_piTechCostMod)									pXML->InitList(&m_piTechCostMod, GC.getNumTechInfos(), 100);
	for (int i = 0; i < GC.getNumTechInfos(); ++i)
	{
		if(getTechCostShift(i)			== 0)				m_piTechCostShift[i]					= pClassInfo->getTechCostShift(i);
		if(getTechCostMod(i)			== 100)				m_piTechCostMod[i]						= pClassInfo->getTechCostMod(i);
	}

parts in CNDr2
Code:
void CvTechInfo::copyNonDefaultsReadPass2(CvTechInfo* pClassInfo, CvXMLLoadUtility* pXML)
{
	bool bOver = pClassInfo->isForceOverwrite();
	CvString cDefault = CvString::format("").GetCString();
	CvWString wDefault = CvWString::format(L"").GetCString();

	for ( int i = 0; i < GC.getNUM_OR_TECH_PREREQS(); i++ )
	{
		if(bOver || pClassInfo->getPrereqOrTechs(i)		!= NO_TECH)	m_piPrereqOrTechs[i]	= pClassInfo->getPrereqOrTechs(i);
	}
	for ( int i = 0; i < GC.getNUM_AND_TECH_PREREQS(); i++ )
	{
		if(bOver || pClassInfo->getPrereqAndTechs(i)	!= NO_TECH)	m_piPrereqAndTechs[i]	= pClassInfo->getPrereqAndTechs(i);
	}
	if(!m_piTechCostShift)											pXML->InitList(&m_piTechCostShift, GC.getNumTechInfos(), 0);
	if(!m_piTechCostMod)											pXML->InitList(&m_piTechCostMod, GC.getNumTechInfos(), 100);
	for (int i = 0; i < GC.getNumTechInfos(); ++i)
	{
		if(bOver || pClassInfo->getTechCostShift(i)		!= 0)		m_piTechCostShift[i]	= pClassInfo->getTechCostShift(i);
		if(bOver || pClassInfo->getTechCostMod(i)		!= 100)		m_piTechCostMod[i]		= pClassInfo->getTechCostMod(i);
	}
}

Since you don't have my bForceOverwrite tag, ignore the bOver references. As you can see, I have the EXACT same code lifted from CNDr2 into CND. I remember this being critical. But the most important part isn't in either of these functions, up in the normal readPass2 function I have:

Code:
	pXML->SetVariableListTagPair(&m_piTechCostShift, "TechCostShifts", sizeof(GC.getTechInfo((TechTypes)0)), GC.getNumTechInfos(), 0);
	pXML->SetVariableListTagPair(&m_piTechCostMod, "TechCostMods", sizeof(GC.getTechInfo((TechTypes)0)), GC.getNumTechInfos(), 100);

The use of SetVariableListTagPair ensures that the arrays are created during this pass, which covers the two I added (note that I had included an initialization method in my CNDr2, this was one of the parts I wrote before realizing that it was redundant, and I just never looked at it again till now)

For the Firaxis arrays which already existed, the important part was added in ::read(pXML) instead:
Code:
	pXML->InitList(&m_piPrereqOrTechs, GC.getNUM_OR_TECH_PREREQS(), -1);
	pXML->InitList(&m_piPrereqAndTechs, GC.getNUM_AND_TECH_PREREQS(), -1);

Again, I cannot remember PRECISELY why this is in pass1 instead of pass2, I would assume it could work in either (in pass2 it would be an ELSE case after the if (gDLL->getXMLIFace()->SetToChildByTagName(pXML->GetXML(),"OrPreReqs")) check. I would have thought this should have been done in pass2 like the others, and maybe it would be better there, but it appears to work here, so that is all that is important. This is quite possibly your issue here.
 
It should be set up so that it is absolutely impossible for the arrays to be NULL going in to CNDr2, so the checks for that ought not to be required, leaving the Asserts in place is a good choice though (however I would encase it all in the assert so you don't have extra lines of code in a non-debug compile)

Here is a sample of an array I have set up in CNDr2:

parts in CND:
Code:
    // Readpass2 stuff
    for ( int j = 0; j < GC.getNUM_OR_TECH_PREREQS(); j++)
    {
        if(getPrereqOrTechs(j)            == NO_TECH)            m_piPrereqOrTechs[j]                    = pClassInfo->getPrereqOrTechs(j);
    }
    for ( int j = 0; j < GC.getNUM_AND_TECH_PREREQS(); j++)
    {
        if(getPrereqAndTechs(j)            == NO_TECH)            m_piPrereqAndTechs[j]                    = pClassInfo->getPrereqAndTechs(j);
    }
    if(!m_piTechCostShift)                                    pXML->InitList(&m_piTechCostShift, GC.getNumTechInfos(), 0);
    if(!m_piTechCostMod)                                    pXML->InitList(&m_piTechCostMod, GC.getNumTechInfos(), 100);
    for (int i = 0; i < GC.getNumTechInfos(); ++i)
    {
        if(getTechCostShift(i)            == 0)                m_piTechCostShift[i]                    = pClassInfo->getTechCostShift(i);
        if(getTechCostMod(i)            == 100)                m_piTechCostMod[i]                        = pClassInfo->getTechCostMod(i);
    }
parts in CNDr2
Code:
void CvTechInfo::copyNonDefaultsReadPass2(CvTechInfo* pClassInfo, CvXMLLoadUtility* pXML)
{
    bool bOver = pClassInfo->isForceOverwrite();
    CvString cDefault = CvString::format("").GetCString();
    CvWString wDefault = CvWString::format(L"").GetCString();

    for ( int i = 0; i < GC.getNUM_OR_TECH_PREREQS(); i++ )
    {
        if(bOver || pClassInfo->getPrereqOrTechs(i)        != NO_TECH)    m_piPrereqOrTechs[i]    = pClassInfo->getPrereqOrTechs(i);
    }
    for ( int i = 0; i < GC.getNUM_AND_TECH_PREREQS(); i++ )
    {
        if(bOver || pClassInfo->getPrereqAndTechs(i)    != NO_TECH)    m_piPrereqAndTechs[i]    = pClassInfo->getPrereqAndTechs(i);
    }
    if(!m_piTechCostShift)                                            pXML->InitList(&m_piTechCostShift, GC.getNumTechInfos(), 0);
    if(!m_piTechCostMod)                                            pXML->InitList(&m_piTechCostMod, GC.getNumTechInfos(), 100);
    for (int i = 0; i < GC.getNumTechInfos(); ++i)
    {
        if(bOver || pClassInfo->getTechCostShift(i)        != 0)        m_piTechCostShift[i]    = pClassInfo->getTechCostShift(i);
        if(bOver || pClassInfo->getTechCostMod(i)        != 100)        m_piTechCostMod[i]        = pClassInfo->getTechCostMod(i);
    }
}
Since you don't have my bForceOverwrite tag, ignore the bOver references. As you can see, I have the EXACT same code lifted from CNDr2 into CND. I remember this being critical. But the most important part isn't in either of these functions, up in the normal readPass2 function I have:

Code:
    pXML->SetVariableListTagPair(&m_piTechCostShift, "TechCostShifts", sizeof(GC.getTechInfo((TechTypes)0)), GC.getNumTechInfos(), 0);
    pXML->SetVariableListTagPair(&m_piTechCostMod, "TechCostMods", sizeof(GC.getTechInfo((TechTypes)0)), GC.getNumTechInfos(), 100);
The use of SetVariableListTagPair ensures that the arrays are created during this pass, which covers the two I added (note that I had included an initialization method in my CNDr2, this was one of the parts I wrote before realizing that it was redundant, and I just never looked at it again till now)

For the Firaxis arrays which already existed, the important part was added in ::read(pXML) instead:
Code:
    pXML->InitList(&m_piPrereqOrTechs, GC.getNUM_OR_TECH_PREREQS(), -1);
    pXML->InitList(&m_piPrereqAndTechs, GC.getNUM_AND_TECH_PREREQS(), -1);
Again, I cannot remember PRECISELY why this is in pass1 instead of pass2, I would assume it could work in either (in pass2 it would be an ELSE case after the if (gDLL->getXMLIFace()->SetToChildByTagName(pXML->GetXML(),"OrPreReqs")) check. I would have thought this should have been done in pass2 like the others, and maybe it would be better there, but it appears to work here, so that is all that is important. This is quite possibly your issue here.

AFAIK, the code you posted, and my code are functionally the same. I do initList in the first read. And my code in CND and CNDr2 is exactly the same...

I ran a debug dll, and it crashes right after triggering both the asserts I added in CNDr2.
 
Hrm... with the initlist properly placed those asserts shouldn't fire off. So something is going wrong at that point.

What you posted here for your CopyNonDefaults was just the initializing of the lists, not also the carrying data from pClassInfo. Without that you should wind up with 0 values where you don't want them. Not the error we have at present, so just keep that in mind in case you do run into that error in the future.

-------------------------------

Ok, so breaking out the CvXMLLoadUtility and looking at precisely what is happening with WoC:

After ReadPass1 has executed and done their CND and everything else, we run to CNDr2 if we are pass 2:

First, we create a blank pClassInfo. Absolutely no data is in this thing.

Now we have this blank thing run readPass2. NOTE: We have NEVER run ::read(pXML) on this thing, so all arrays need to be initialized during pass 2 if they are to be utilized in CNDr2 later.

Now we check dependency junk, ignoring that.

Odd, now if we clear out dependencies, we run read(pXML)... AFTER having done pass2. I am lost on that one. Shouldn't matter in the end though since we trash this data.

Now we use the Type to look up what the enum for this item is. At this point it absolutely DOES exist, since we have already run read(pXML) on this earlier, when we didn't know yet if it was going to do a pass2 at all.

Now we use the enum to look up the stored data for this object, and we pass our pClassInfo to the pre-existing data so that it can copy out whatever it requires.

Now we delete our strange backward read data for good.

-----------------------------


Ok, so there is pass2 of WoC. I guess if you want pass1 as well sometime I can do that for you as well, don't much feel like it at the moment. Basically the same thing, but at the end it works the other way around (you copy the preexisting data into the stuff you just loaded, then overwrite what had already existed before)

So that is why the init in pass1 works fine, because it is executed after pass2 is in the modules.


In CvXMLLoadUtilitySet.cpp, place a breakpoint on the last of these lines:

Code:
					T* pClassInfo = new T;

					FAssert(NULL != pClassInfo);
					
					//Second readpass information is loaded into a completely new set of data
					bool bSuccess = pClassInfo->readPass2(this);

It'll fire off quite a bit (every XML object you have in every module), so leave it disabled initially, and have one enabled at the start of CvCivicInfo::read(pXML). When that one fires, you are now loading civics, so go turn on the one in CvXMLLoadUtilitySet so you can see the pass2 stuff coming up. When this breakpoint fires, you haven't run pass2 for the module yet, so step forward one line (step over, not step into) and then do a mouseover on pClassInfo and check the TYPE so you know which item you are loading. Once you have noted what is loading, let the code resume. This way you can figure out which XML item is actually loading when you get your crash. Might help to narrow down the actual issue, at the least it allows you to walk through step by step with the guilty party and see precisely what happens before the crash, might help as well.

Oh, when placing the breakpoint in CvXML, don't use the comment to find the code area, that is probably my comment, so won't be in your code.
 
I just realized something, something that threw me off when I tried to merge your WoC changes to RevDCM. You completely changed the parameters, which through me off. Standard WoC only has (CvCivicInfo* pClassInfo) as a parameter for CND and CNDr2. You added the pXML part. Which means that our CND will look nothing alike, since your's can do regular XML loading too, and ours can not. Which may be a problem.

However, I am going to start the debug dll and will post the results in a bit.
 
I put the breakpoint here:

Code:
void CvXMLLoadUtility::SetGlobalClassInfo(std::vector<T*>& aInfos, const char* szTagName, bool bTwoPass)
{
...
					SkipToNextVal();	// skip to the next non-comment node

					T* pClassInfo = new T;

					FAssert(NULL != pClassInfo);

					[COLOR="Red"]bool bSuccess = pClassInfo->readPass2(this);[/COLOR]
...
}

But it never triggered.
 
I forgot about my little tweak to CNDr2. I am really not sure why they hadn't done that themselves, so many good functions in CvXML which are worth getting your hands on.

You placed a normal, no condition breakpoint? Very strange that it wouldn't trigger in that location.

Try one here so we can see if the function is ever being used, and takes bTwoPass as true any of those times. This ought to be called for nearly every XML file:

Code:
		} while (gDLL->getXMLIFace()->NextSibling(m_pFXml));

[COLOR="Red"]		if (bTwoPass)
[/COLOR]		{
			if (gDLL->getXMLIFace()->LocateNode(m_pFXml, szTagName))
			{
				// loop through each tag
				do
				{
					if (!SkipToNextVal())	// skip to the next non-comment node
					{
						sprintf(szLog, "Empty XML File (%s), last Type loaded was %s", szTagName, mszLastType.GetCString());
						FAssertMsg(false, szLog);
						break;
					}

					T* pClassInfo = new T;

					FAssert(NULL != pClassInfo);
					
					bool bSuccess = pClassInfo->readPass2(this);

Or maybe I am in the wrong location completely, so I'll start from CivicInfos:

Code:
	LoadGlobalClassInfo(GC.getCivicInfo(), "CIV4CivicInfos", "GameInfo", "Civ4CivicInfos/CivicInfos/CivicInfo", false, &CvDLLUtilityIFaceBase::createCivicInfoCacheObject);

that is loaded through:

Code:
void CvXMLLoadUtility::LoadGlobalClassInfo(std::vector<T*>& aInfos, const char* szFileRoot, const char* szFileDirectory, const char* szXmlPath, bool bTwoPass, CvCacheObject* (CvDLLUtilityIFaceBase::*pArgFunction) (const TCHAR*))

which calls up:

Code:
			SetGlobalClassInfo(aInfos, szXmlPath, bTwoPass);

And that points to the function we are doing now. Same exact call is used for basic XML loading and modular variation. As long as you have:

Code:
	bool bTwoPassReplacement = false; // Use this if you wanna use the regular Firaxis bTwoPass

set as false still.
 
Okay, I did all of that, and looked at the pClassInfo when it triggeded the breakpoint, right after the civic loadGlobalClassInfo was triggered. It loaded the base XML file, and they had the correct XML values for everything, but it crashed before it loaded the modular civic XML.
 
At this point, I am totally confused, and this is completely a WoC problem. Xienwolf, if I gave you the base BTS sources, do you think you could make them work with your SDK? If you were willing and could, we could probably port that over to my SDK then.
 
I'm actually in the middle of completely rebuilding my code right now, so at the moment my DLL doesn't have WoC, though integrating that again is my next step. This is code which would be worth having around for my mod as well, so I can take a crack at it with my old code base, which still does have WoC going.

So I guess go ahead and check the code real quick to make certain is compares cleanly to base BtS sources and has an easily searched tag and then post it up, I'll see if I can get it working with WoC. May take a few days as I don't get much time to code lately, but break is coming up so I may find some more time then (not too likely however, as I'll be showing off my son to my extended family)
 
I'm actually in the middle of completely rebuilding my code right now, so at the moment my DLL doesn't have WoC, though integrating that again is my next step. This is code which would be worth having around for my mod as well, so I can take a crack at it with my old code base, which still does have WoC going.

So I guess go ahead and check the code real quick to make certain is compares cleanly to base BtS sources and has an easily searched tag and then post it up, I'll see if I can get it working with WoC. May take a few days as I don't get much time to code lately, but break is coming up so I may find some more time then (not too likely however, as I'll be showing off my son to my extended family)

It's been neatly documented, searching for "Afforess" should yield all of the changes. It's Base BTS 3.19 source code.

View attachment CivicAttitudeChange Sources.rar
 
Ok, I have your code up and running properly in my non-WoC build now, cleaned up your text key a little bit, leaving comments in the code as I go through so that you get them all at once and while you are looking at the code (assuming I manage to fix it of course). If I don't get the WoC parts working today, then it'll probably be a while till I manage to get time to code once again.
 
Ok, I have your code up and running properly in my non-WoC build now, cleaned up your text key a little bit, leaving comments in the code as I go through so that you get them all at once and while you are looking at the code (assuming I manage to fix it of course). If I don't get the WoC parts working today, then it'll probably be a while till I manage to get time to code once again.

FYI, It appears that my AI logic for the civic attitude changes causes a CTD after each turn, I'd comment it out until we figure out exactly what is broken in it.
 
It worked fine for me. I ran a 100 turn test once I had things up and working in the non-WoC.

Found one issue with the code so far while debugging. Remains to be seen if that was THE issue. But you were using gc.getNumCivicInfos() during read(pXML) of Civics. When that runs for the first item it is a negative number, creating an array of negative size is bad juju.
 
Ok, the bit in ::read(pXML) was probably an issue, but most likely a minor one since in readPass2 you ought to have overwritten the smaller/wrong array.

In ::readPass2 you had the ELSE statement to initialize the arrays if the tag isn't found, but you forgot to include the text array once you had added it. I am almost entirely certain that was the only change which was required. Here are my files anyway, everything should be marked with your name. I didn't wind up changing too much (cleaned up the text key, removed the ::read(pXML) portion, and added one more initList to your ::readPass2 portion), and should have only added those 2 extra sections in CvInfos to handle the WoC stuff.

Also, I am no longer certain that the lines in CND (pass1) are required, but I didn't test it. Actually I didn't test any of this with an actual module loading at all. I was able to replicate the crash without using any modules (turns out that CNDr2 runs even without any module affecting the code, but CND itself apparently doesn't suffer from that issue. Bears further investigation, but not right now, and not relevant to your specific issue)
 

Attachments

Ok, the bit in ::read(pXML) was probably an issue, but most likely a minor one since in readPass2 you ought to have overwritten the smaller/wrong array.

In ::readPass2 you had the ELSE statement to initialize the arrays if the tag isn't found, but you forgot to include the text array once you had added it. I am almost entirely certain that was the only change which was required. Here are my files anyway, everything should be marked with your name. I didn't wind up changing too much (cleaned up the text key, removed the ::read(pXML) portion, and added one more initList to your ::readPass2 portion), and should have only added those 2 extra sections in CvInfos to handle the WoC stuff.

Also, I am no longer certain that the lines in CND (pass1) are required, but I didn't test it. Actually I didn't test any of this with an actual module loading at all. I was able to replicate the crash without using any modules (turns out that CNDr2 runs even without any module affecting the code, but CND itself apparently doesn't suffer from that issue. Bears further investigation, but not right now, and not relevant to your specific issue)

Alas, I am running into the same problem as before... Whoever merged WoC in with RevDCM should have talked to you first. My CND only have the 1 parameter, so I can not call initList(...) in them. I compared how you did the PrereqProjects in CvProjectInfos, since that is a readpass2 array as well. Your CND looks like this:

Code:
if(!m_piProjectsNeeded)
        pXML->InitList(&m_piProjectsNeeded, GC.getNumProjectInfos(), 0);
    for (int i = 0; i < GC.getNumProjectInfos(); ++i)
    {
        if(getProjectsNeeded(i) == 0)
            m_piProjectsNeeded[i] = pClassInfo->getProjectsNeeded(i);
    }

But mine looks like this:

Code:
    if ( m_piProjectsNeeded == NULL)
    {
        m_piProjectsNeeded = new int[GC.getNumProjectInfos()];
        for ( int i = 0; i < GC.getNumProjectInfos(); i++)
            m_piProjectsNeeded[i] = 0;
    }
    for ( int i = 0; i < GC.getNumProjectInfos(); i++)
    {
        if(getProjectsNeeded(i) == 0)
            m_piProjectsNeeded[i] = pClassInfo->getProjectsNeeded(i);
    }

I think they are functionally the same. My version checks to see if it is Null, and if it is, creates a new array and fills it with 0's. Yours initializes an array with 0's.

So I followed a similar style with your CND to see if I could get them in a similar form, and came up with this:

Code:
if(m_piCivicAttitudeChanges == NULL)
    {
        m_piCivicAttitudeChanges = new int[GC.getNumCivicInfos()];
        for ( int i = 0; i < GC.getNumCivicInfos(); i++)
            m_piCivicAttitudeChanges[i] = 0;
    }
    for ( int i = 0; i < GC.getNumCivicInfos(); i++)
    {
        if (pClassInfo->getCivicAttitudeChange(i) == 0)
            m_piCivicAttitudeChanges[i] = pClassInfo->getCivicAttitudeChange(i);
    }
    if(m_pszCivicAttitudeReason == NULL)
    {
        m_pszCivicAttitudeReason = new CvString[GC.getNumCivicInfos()];
        for ( int i = 0; i < GC.getNumCivicInfos(); i++)
            m_pszCivicAttitudeReason[i] = 0;
    }
    for ( int i = 0; i < GC.getNumCivicInfos(); i++)
    {
        if (pClassInfo->getCivicAttitudeReason(i) == 0)
            m_pszCivicAttitudeReason[i] = pClassInfo->getCivicAttitudeReason(i);
    }

Which looks like it should work to me, but gives me a CTD when InitXML.

At first, I was going to just add your CvXMLLoadUtility* pXML parameter, that my CND are missing, and do it your way, but then I realized that I don't think WoC loads our CND the same, and if I add the parameter, it will just crash as well. I'd imagine you'd need to look at RevDCM's WoC in detail to tell.

It feels like I got all the worst features of WoC, but without any of the real benefits like you have, with the bForceOverwrite tag and extra parameters. :mad:
 
Back
Top Bottom