Civic Attitude Modifier

5 lines after your first breakpoint you have a setToParent which doesn't look to be in the right place to me. But it has been a while since I have looked at the array loading routines in detail. Your code that is showing does look similar to CivilizationBuildings at the least, and that setToParent is in roughly the same spot.

By the time you reach the first breakpoint, the array should be the proper length, and full of zero values (or whatever you set for a default). In the second breakpoint though, the breakpoint happens BEFORE the line runs, so the array should still be junk at that point, let it run 1 more line and THEN I would expect it to be properly initialized. Third breakpoint shows what we are seeing as an ingame result, the array isn't the proper length, so junk is being read out of it. Hence the ability to have such massive flips (from absolute hatred to absolute love), all values are MAX_INT positive or negative.
 
5 lines after your first breakpoint you have a setToParent which doesn't look to be in the right place to me. But it has been a while since I have looked at the array loading routines in detail. Your code that is showing does look similar to CivilizationBuildings at the least, and that setToParent is in roughly the same spot.

By the time you reach the first breakpoint, the array should be the proper length, and full of zero values (or whatever you set for a default). In the second breakpoint though, the breakpoint happens BEFORE the line runs, so the array should still be junk at that point, let it run 1 more line and THEN I would expect it to be properly initialized. Third breakpoint shows what we are seeing as an ingame result, the array isn't the proper length, so junk is being read out of it. Hence the ability to have such massive flips (from absolute hatred to absolute love), all values are MAX_INT positive or negative.

I'm thinking the issue has to do with this section of code in CvInfos

Code:
  // create arrays to hold attitude changes and messages
    pXML->InitList(&m_piCivicAttitudeChanges, GC.getNumCivicInfos(), 0);
    //pXML->InitList(&m_pszCivicAttitudeTextKeys, GC.getNumCivicInfos(), "");

It doesn't look quite right to me, comparing it to other arrays. Also, for fun, I commented out the InitList for CivicAttitudeChanges, and the array was forced to be 0. It stopped the garbage (The AI were cautious toward me), but the XML changes were ignored.
 
Add some debug logging there that writes out GC.getNumCivicInfos(). I wonder if perhaps it is creating a size zero array. The whole point of doing this in the second pass is so that the civics have been read. Is the above code from readPass2() or read()? It should be in the former.

When you say it doesn't match other array initialization, can you post one that it doesn't match? How exactly does it not match?
 
Hadn't noticed that before, you are initializing the list to the total number of civics during readpass1, at which point there aren't the proper number of civics yet. So that does need to be eliminated. Instead, at the start of CopyNonDefaultsReadpass2, place a check for this array == NULL, and if it is, THEN run initlist on it. Having a non-null array in CNDr2 is what that init was meant for IIRC.
 
Now that my current projects are out of the way for now, I sat down and though about this one again. What are we doing here. Well, Isn't this really another Array of an Array? An array of civic infos, and related civic attitudes? Well, I compared the code to the way the TAB Modcomp has its sources, and it uses 3 arrays of arrays. They way they are set up (And they work, mind you, I merged them in and use them.) is completely different than what we have set up. Should I try and mimic what TAB modcomp did for arrays of array's instead of doing it this way with a readpass2?
 
These are not arrays of arrays. Each single CvCivicInfo has an array of attitudes, one value for each CivicTypes. This amounts to a single array. It needs another separate array to hold the messages for each CivicTypes, but this again is a single array. Two arrays don't amount to an array of arrays.
 
These are not arrays of arrays. Each single CvCivicInfo has an array of attitudes, one value for each CivicTypes. This amounts to a single array. It needs another separate array to hold the messages for each CivicTypes, but this again is a single array. Two arrays don't amount to an array of arrays.

Well, I understand now; now that I know about parallel arrays anyway. :)

I was reflecting on this, and something Xienwolf said about World of Civilization came to mind. He said that WoC "wrecked" the ability to add readpass2's because of the way it worked. I wonder if this is the real underlying cause of the problem.
 
I'd have to see his comment in context. Is this from the discussion we just had about sorting items after the first read pass?
 
I'd have to see his comment in context. Is this from the discussion we just had about sorting items after the first read pass?

Perhaps I was wrong. Here is his comment.

Since WoC doesn't sort buildings, it should still work...

I'm gonna try doing this on a clean 3.19 SDK this weekend, and see if it makes a difference.
 
The Best News Possible!

It WORKS!!!!

I redid it with base BTS SDK and got it working! Now all that's left is to do the other array and get the text to work!

You can test out the mod, slavery gives a -5 attitude with other players. It makes them "annoyed" at you.

So, let's get cracking. EmperorFool, I've never dealt with String arrays before (at least not in C++), you're going to need to hand hold me a bit.

View attachment Mod.rar
 
Aren't you already loading the CivicTypes XML string keys into an array and then looking them up in readpass2()? Or are you reading them directly in rp2()? Hmm, I think the latter. You'd only need to store as string and lookup later if you were doing it in readpass3().

However, you won't need to look up the strings anywhere else--just store them into a CvString array is my guess. You should be able to use an example from CvInfoBase, for example szCivilopedia. This will work the same way except you'll store an array of them instead of just a single one.

Give it a shot and post what you get. I think this will be easier than you expect. :)
 
Okay, I'm completely lost. I created another array of const wchar*, and tried to model is after some things I already knew, but the compiler gave me 13 different errors, none of which I understand nor know how to resolve.

Here's my code in CvInfos:

Code:
CvCivicInfo::CvCivicInfo() :

m_piCivicAttitudeChanges(NULL),
m_pszCivicAttitudeReason(NULL),

...

CvCivicInfo::~CvCivicInfo()
{
...
	SAFE_DELETE_ARRAY(m_piCivicAttitudeChanges);
	SAFE_DELETE_ARRAY(m_pszCivicAttitudeReason);
...

int* CvCivicInfo::getCivicAttitudeChanges() const
{
	return m_piCivicAttitudeChanges;
}

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;
}

const wchar* CvCivicInfo::getCivicAttitudeReason(int i) const
{
	FAssertMsg(i < GC.getNumCivicInfos(), "Index out of bounds");
	FAssertMsg(i > -1, "Index out of bounds");
	return m_pszCivicAttitudeReason ? m_pszCivicAttitudeReason[i] : -1;
}
....
void CvCivicInfo::read(FDataStreamBase* stream)
{
...
	SAFE_DELETE_ARRAY(m_piCivicAttitudeChanges);
	m_piCivicAttitudeChanges = new int[GC.getNumCivicInfos()];
	stream->Read(GC.getNumCivicInfos(), m_piCivicAttitudeChanges);
	
	SAFE_DELETE_ARRAY(m_pszCivicAttitudeReason);
	m_pszCivicAttitudeReason = new CvString[GC.getNumCivicInfos()];
	stream->ReadString(GC.getNumCivicInfos(), m_pszCivicAttitudeReason);
...

void CvCivicInfo::write(FDataStreamBase* stream)
{
...
	stream->Write(GC.getNumCivicInfos(), m_piCivicAttitudeChanges);
	stream->WriteString(GC.getNumCivicInfos(), m_pszCivicAttitudeReason);
...
bool CvCivicInfo::read(CvXMLLoadUtility* pXML)
{....
	pXML->InitList(&m_piCivicAttitudeChanges, GC.getNumCivicInfos(), 0);
	pXML->InitList(&m_pszCivicAttitudeReason, GC.getNumCivicInfos(), "");
...

bool CvCivicInfo::readPass2(CvXMLLoadUtility* pXML)
{
	if (!CvInfoBase::read(pXML))
	{
		return false;
	}

	if (gDLL->getXMLIFace()->SetToChildByTagName(pXML->GetXML(),"CivicAttitudeChanges"))
	{
		//gDLL->MessageBox("Found CivicAttitudeChanges", "readPass2");
		if (pXML->SkipToNextVal())
		{
		//	gDLL->MessageBox("Found first CivicAttitudeChange", "readPass2");
			pXML->InitList(&m_piCivicAttitudeChanges, GC.getNumCivicInfos(), 0);

			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 (pXML->SkipToNextVal())
					{
						if (pXML->GetChildXmlVal(szCivicKey))
						{
							char	szMessage[1024];
							sprintf(szMessage, "Civic: %s", szCivicKey);
							//gDLL->MessageBox(szMessage, "readPass2");
							int iCivicType = pXML->FindInInfoClass(szCivicKey);
							if (iCivicType >= 0)
							{
								pXML->GetNextXmlVal(&m_piCivicAttitudeChanges[iCivicType]);
								pXML->GetNextXmlVal(&m_pszCivicAttitudeReason[iCivicType]);
								gDLL->getXMLIFace()->SetToParent(pXML->GetXML());
								char	szMessage[1024];
								sprintf(szMessage, "Modifier: %d", m_piCivicAttitudeChanges[iCivicType]);
								//gDLL->MessageBox(szMessage, "readPass2");
							}

							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());
	}
	else
	{
		pXML->InitList(&m_piCivicAttitudeChanges, GC.getNumCivicInfos(), 0);
	}

	return true;
}

The compiling errors I am getting are:
1>CvInfos.cpp(5322) : error C2436: 'm_pszCivicAttitudeReason' : member function or nested class in constructor initializer list
1>CvInfos.cpp(5348) : warning C4551: function call missing argument list
1>CvInfos.cpp(5348) : error C2541: 'delete' : cannot delete objects that are not pointers
1>CvInfos.cpp(5348) : error C2659: '=' : overloaded function as left operand
1>CvInfos.cpp(5693) : warning C4551: function call missing argument list
1>CvInfos.cpp(5693) : error C2109: subscript requires array or pointer type
1>CvInfos.cpp(5838) : warning C4551: function call missing argument list
1>CvInfos.cpp(5838) : error C2541: 'delete' : cannot delete objects that are not pointers
1>CvInfos.cpp(5838) : error C2659: '=' : overloaded function as left operand
1>CvInfos.cpp(5839) : error C2659: '=' : overloaded function as left operand
1>CvInfos.cpp(5840) : error C2664: 'unsigned int FDataStreamBase::ReadString(int,std::string [])' : cannot convert parameter 2 from 'const wchar **(int) const ' to 'std::string []'
1> There is no context in which this conversion is possible
1>CvInfos.cpp(5956) : error C2664: 'void FDataStreamBase::Write(int,const char [])' : cannot convert parameter 2 from 'const wchar **(int) const ' to 'const char []'
1> There is no context in which this conversion is possible
1>CvInfos.cpp(6114) : error C2276: '&' : illegal operation on bound member function expression
1>CvInfos.cpp(6222) : error C2109: subscript requires array or pointer type
 
I need to see the code for m_pszCivicAttitudeReason in CvInfos.h.

The string code was mostly just-guess work on my part. It's an array of references, yes?

Code:
public:

    CvCivicInfo();

    int getCivicAttitudeChange(int i) const;
    int* getCivicAttitudeChanges() const;
    
    const wchar* getCivicAttitudeReason(int i) const;
    
    bool readPass2(CvXMLLoadUtility* pXML);

protected:

    int* m_piCivicAttitudeChanges;
    
    const wchar** m_pszCivicAttitudeReason(int i) const;
 
I'm not sure about how "const wchar**" will be parsed: as an array of const wchar pointers or a constant pointer to wchar pointers. If the latter, you'll have problems. Luckily I believe that ()s can save the day:

Code:
(const wchar*)* m_pszCivicAttitudeReason;

Drop the "(int i) const"--that denotes a function declaration but we want a variable here.
 
I'm not sure about how "const wchar**" will be parsed: as an array of const wchar pointers or a constant pointer to wchar pointers. If the latter, you'll have problems. Luckily I believe that ()s can save the day:

Code:
(const wchar*)* m_pszCivicAttitudeReason;
Drop the "(int i) const"--that denotes a function declaration but we want a variable here.

Okay, I changed that, but now I get these compiler errors:

1>CvInfos.cpp(5693) : error C2440: 'return' : cannot convert from 'const wchar **const ' to 'const wchar *'
1> Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
1>CvInfos.cpp(5840) : error C2664: 'unsigned int FDataStreamBase::ReadString(int,std::string [])' : cannot convert parameter 2 from 'const wchar ** ' to 'std::string []'
1> Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
1>CvInfos.cpp(5956) : error C2664: 'unsigned int FDataStreamBase::WriteString(int,std::string [])' : cannot convert parameter 2 from 'const wchar ** ' to 'std::string []'
1> Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
1>CvInfos.cpp(6114) : error C2782: 'void CvXMLLoadUtility::InitList(T ** ,int,T)' : template parameter 'T' is ambiguous
1> c:\Users\Cameron\Desktop\CvGameCoreDLL\CvXMLLoadUtility.h(182) : see declaration of 'CvXMLLoadUtility::InitList'
1> could be 'const char *'
1> or 'const wchar *'
1>CvInfos.cpp(6222) : error C2664: 'bool CvXMLLoadUtility::GetNextXmlVal(std::string &,char *)' : cannot convert parameter 1 from 'const wchar ** ' to 'std::string &'
1> A reference that is not to 'const' cannot be bound to a non-lvalue
 
In looking at the existing code (I'm basing it off the szCivilopedia) I see they store the strings as CvString. Only when returning a single string does it switch to a const wchar *. You should do this as well.

Code:
CvString* m_pszCivicAttitudeReason;

It looks like that's what you are doing in the cpp code, so just change this in the header. Also, I can't tell what line numbers those are since I don't have the full file, so you need to post the code with the lines containing errors in bold or post the whole file.
 
In looking at the existing code (I'm basing it off the szCivilopedia) I see they store the strings as CvString. Only when returning a single string does it switch to a const wchar *. You should do this as well.

Code:
CvString* m_pszCivicAttitudeReason;
It looks like that's what you are doing in the cpp code, so just change this in the header. Also, I can't tell what line numbers those are since I don't have the full file, so you need to post the code with the lines containing errors in bold or post the whole file.

Okay, I'm down to only 5 compiler errors:

First one has to do with this function:

Code:
CvString CvCivicInfo::getCivicAttitudeReason(int i) const
{
    FAssertMsg(i < GC.getNumCivicInfos(), "Index out of bounds");
    FAssertMsg(i > -1, "Index out of bounds");
  [COLOR=Red]  return m_pszCivicAttitudeReason;[/COLOR]
}

error C2664: 'CvString::CvString(int)' : cannot convert parameter 1 from 'CvString *const ' to 'int'
1> This conversion requires a reinterpret_cast, a C-style cast or function-style cast

These:

1> c:\Users\Cameron\Desktop\CvGameCoreDLL\CvXMLLoadUtility.h(182) : see declaration of 'CvXMLLoadUtility::InitList'
1> could be 'const char *'
1> or 'CvString'
1>CvInfos.cpp(6222) : error C2664: 'bool CvXMLLoadUtility::GetNextXmlVal(std::string &,char *)' : cannot convert parameter 1 from 'CvString *' to 'std::string &'
1> A reference that is not to 'const' cannot be bound to a non-lvalue

Have to do with this:
Code:
pXML->InitList(&m_pszCivicAttitudeReason, GC.getNumCivicInfos(), "");

and

Code:
pXML->GetNextXmlVal(&m_pszCivicAttitudeReason[iCivicType]);
 
I could have sworn you had this correct above:

Code:
return m_pszCivicAttitudeReason[B][COLOR="Red"][i][/COLOR][/B];

I think this line

Code:
pXML->InitList(&m_pszCivicAttitudeReason, GC.getNumCivicInfos(), "");

is unnecessary since CvStrings are automatically initialized to be empty ("").

Finally, remove the & from that last line:

Code:
pXML->GetNextXmlVal([B][COLOR="Red"][s]&[/s][/COLOR][/B]m_pszCivicAttitudeReason[iCivicType]);

The function is declared to take a reference which works just like a pointer except that you don't take the address using & when calling it. The compiler takes care of that for you.
 
Back
Top Bottom