Civic Attitude Modifier

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.

haha! It compiled.

Now, I need to make it loop for the right civic combo and pull out the correct string for the attitude, which will all be done in CvGameTextMgr, correct?

A sidenote, my poor laptop doesn't like me compiling two dll's at once.
 
I'm not sure how to do the CvGameTextMgr code... I know it goes in CvGameTextMgr::getAttitudeString(), but it's unlike anything I've done before...
 
You already have the code that calculates the modifiers for any two players, right?

  1. Copy that code into CvGTM::getAttitudeString() wherever you'd like them to show up in the order of modifiers.
  2. At the part where you += the individual modifier, change the line to do a szBuffer.append(...)--or whatever the CvWString that is holding the output.
  3. The "..." should pass the value returned from your new array in CvCivicInfo to getText() just as other code passes in "TXT_KEY_..." strings.
 
You already have the code that calculates the modifiers for any two players, right?

  1. Copy that code into CvGTM::getAttitudeString() wherever you'd like them to show up in the order of modifiers.
  2. At the part where you += the individual modifier, change the line to do a szBuffer.append(...)--or whatever the CvWString that is holding the output.
  3. The "..." should pass the value returned from your new array in CvCivicInfo to getText() just as other code passes in "TXT_KEY_..." strings.

Okay, I added this to that function, but I get compiler errors:

Code:
   for (iI = 0; iI < GC.getNumCivicInfos(); iI++)
    {
        for (int iJ = 0; iJ < GC.getNumCivicInfos(); iJ++)
        {
            if (GET_PLAYER(ePlayer).AI_getCivicAttitudeChange(eTargetPlayer) != 0)
            {
                szBuffer.append(NEWLINE);
                [COLOR=Red]szBuffer.append(GC.getCivicInfo((CivicTypes)iI).getCivicAttitudeReason(GC.getCivicInfo((CivicTypes)iJ)));[/COLOR]
            }
        }
    }

1>CvGameTextMgr.cpp(10715) : error C2664: 'CvCivicInfo::getCivicAttitudeReason' : cannot convert parameter 1 from 'CvCivicInfo' to 'int'
1> No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
 
You want to just pass in a CivicTypes to getAttitudeReason() instead of a CvCivicInfo, but this code won't work now that I look at it. The problem is that the attitude modifier a) might be 0 if you have two civic combos that cancel each other out, and more importantly b) will be the same value for each combo you check.

Instead you need to loop over the civics that the players are running, kinda. If my memory serves, civic attitude modifiers are one-way, right? What I mean is that e.g. running Slavery causes a -5 hit for anyone running Emancipation, but the reverse may be different. And the modifier in this case would be on Slavery, right? (I think this may be backwards, but I don't wanna reread the whole thread).

If that's the case, you need to run through the civics that eTargetPlayer is running. For each one, test if that civic has a modifier when paired with the civic ePlayer is running.

Code:
for each civic option type (government, legal, religion, etc)
    eTargetCivic = eTargetPlayer's civic for the type
    eCivic = ePlayer's civic for the type
    if modifier for eTargetCivic + eCivic is not zero
        add specific message
 
Okay, I'm sure I did it wrong, since I get a CTD when Init XML. Hopefully you can point out the errors in it:
Code:
CivicTypes eTargetCivic;
	int iCivic;
	for (int iJ = 0; iJ < GC.getNumCivicOptionInfos(); iJ++)
	{
		for (iI = 0; iI < GC.getNumCivicInfos(); iI++)
		{
			if (GET_PLAYER(eTargetPlayer).getCivics((CivicOptionTypes)iJ) == (CivicTypes)iI)
			{
				eTargetCivic = CivicTypes(iI);
			}
			if (GET_PLAYER(ePlayer).getCivics((CivicOptionTypes)iJ) == (CivicTypes)iI)
			{
				iCivic = iI;
			}
		}
		if (GC.getCivicInfo(eTargetCivic).getCivicAttitudeChange((CivicTypes)iCivic) != 0)
		{
			szBuffer.append(NEWLINE);
			szBuffer.append(GC.getCivicInfo(eTargetCivic).getCivicAttitudeReason(iCivic));
		}
	}
 
This code isn't run during XML initialization. It is only executed when you hover over a leaderhead. Get your game loading first before we try to debug this code (that may or may not be correct). Clean build?
 
This code isn't run during XML initialization. It is only executed when you hover over a leaderhead. Get your game loading first before we try to debug this code (that may or may not be correct). Clean build?

Rebuilding did not fix anything. I assume it is because of the changes to CvInfos. Time for a debug build...
 
A Debug build revealed nothing. It just crashed with no asserts or anything, breaking in the exe.

I assume it has to do with the changes to CvInfos, adding the CivicAttitudeReason array, but I don't know why.
 
Comment out lines until you find which one is the cause. Or use the interactive debugger to step through the code as it's reading. Just set a breakpoint at the start of where you read into the arrays, and make sure to put a modifier on the first civic to make it easier to get there.
 
The problem is this line:

Code:
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]);
								[COLOR="Red"]pXML->GetNextXmlVal(m_pszCivicAttitudeReason[iCivicType]);[/COLOR]
								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;
}

When I comment it out, I can load again.
 
Ah, you are not creating the array. Notice that you call InitList() in read() for both arrays, but at this point getNumCivicInfos() returns 0 (about to read civics). Then in readpass2() you only initialize m_piCivicAttitudeChanges--not the other one.

You need to add

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

to readpass2().
 
I'm getting an error compiling:

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 'CvString'

This is because the function "InitList" is overloaded, and both parameters are possible, so it doesn't know which to go with, correct? Well, at least I understand the problem, but what's the solution?
 
Okay, it compiles and the game starts fine. However, no diplomacy hover text appears, despite the fact that I updated the Schema and XML files and added some.

I'm assuming that it has to do with my faulty CvGameTextMgr logic:

Code:
CivicTypes eTargetCivic;
	int iCivic;
	for (int iJ = 0; iJ < GC.getNumCivicOptionInfos(); iJ++)
	{
		for (iI = 0; iI < GC.getNumCivicInfos(); iI++)
		{
			if (GET_PLAYER(eTargetPlayer).getCivics((CivicOptionTypes)iJ) == (CivicTypes)iI)
			{
				eTargetCivic = CivicTypes(iI);
			}
			if (GET_PLAYER(ePlayer).getCivics((CivicOptionTypes)iJ) == (CivicTypes)iI)
			{
				iCivic = iI;
			}
		}
		if (GC.getCivicInfo(eTargetCivic).getCivicAttitudeChange((CivicTypes)iCivic) != 0)
		{
			szBuffer.append(NEWLINE);
			szBuffer.append(GC.getCivicInfo(eTargetCivic).getCivicAttitudeReason(iCivic));
		}
	}
 
You will also have an issue with the line you marked in red due to the missing &

Actually, this is covered by my earlier post; & causes a compiler error since the parameter is specified as a reference in the function declaration.

@Afforess - I think it's time to start taking some of the training wheels off this bike. :) You have several tools under your belt to help you figure out what's wrong here: adding debugging output either to the log or by adding text to the hover, and using the debugger to step through the code as it runs. My one hint: is the targeting backwards?

I will make one comment about your logic. While it seems to be sound, it's overly complicated. This block and the same one immediately after it

Code:
for (iI = 0; iI < GC.getNumCivicInfos(); iI++)
{
	if (GET_PLAYER(eTargetPlayer).getCivics((CivicOptionTypes)iJ) == (CivicTypes)iI)
	{
		eTargetCivic = CivicTypes(iI);
	}
}

are essentially doing this

Code:
for each x
    if [B][COLOR="Red"]foo()[/COLOR][/B] = x
        z = x

You can replace that entire block with

Code:
z = foo()

or in this case

Code:
eTargetCivic = GET_PLAYER(eTargetPlayer).getCivics((CivicOptionTypes)iJ);

The key to see this for yourself is to note that the bold part above does not change inside the loop. You can do the same thing to calculate iCivic as well, though I would call it eCivic instead of iCivic since it will hold a CivicTypes value.
 
@Afforess - I think it's time to start taking some of the training wheels off this bike. :) You have several tools under your belt to help you figure out what's wrong here: adding debugging output either to the log or by adding text to the hover, and using the debugger to step through the code as it runs. My one hint: is the targeting backwards?

I will make one comment about your logic. While it seems to be sound, it's overly complicated. This block and the same one immediately after it

Code:
for (iI = 0; iI < GC.getNumCivicInfos(); iI++)
{
    if (GET_PLAYER(eTargetPlayer).getCivics((CivicOptionTypes)iJ) == (CivicTypes)iI)
    {
        eTargetCivic = CivicTypes(iI);
    }
}
are essentially doing this

Code:
for each x
    if [B][COLOR=Red]foo()[/COLOR][/B] = x
        z = x
You can replace that entire block with

Code:
z = foo()
or in this case

Code:
eTargetCivic = GET_PLAYER(eTargetPlayer).getCivics((CivicOptionTypes)iJ);
The key to see this for yourself is to note that the bold part above does not change inside the loop. You can do the same thing to calculate iCivic as well, though I would call it eCivic instead of iCivic since it will hold a CivicTypes value.

Okay, well the reason I was using iCivic instead of eCivic was because getCivicAttitudeChange's parameter is an int, not a civictype.
 
Okay, well the reason I was using iCivic instead of eCivic was because getCivicAttitudeChange's parameter is an int, not a civictype.

That's okay. C++ will automatically cast from an enumeration such as CivicTypes or PlayerTypes to an int without requiring a cast. It's only when going the other way that it makes you manually cast the int using "(CivicTypes)".

BTW, this casting doesn't actually do anything--the compiler makes you do it so that you have to make a conscious decision to treat a random int as a constrained value like CivicTypes. It will merrily allow you to cast an invalid value like -2372 to a PlayerTypes.

Any luck on finding what's wrong with the code? It looks logically fine here. My only guess is that you have the targets backwards, i.e. look up eCivic and check it for getCivicAttitudeReason(eTargetCivic) rather then the other way around.

Also, right now you should see the XML Text key in the hover: TXT_KEY_CIVIC_ATTITUDE_EMANCIPATION_HATES_SLAVERY or whatever you use. You'll need to eventually look up the translated phrase using that key. Right now, however, you just need to get your code detecting the modifiers. Given that the code in CvPlayer::getCivicAttitudeModifier() works, this should as well.
 
That's okay. C++ will automatically cast from an enumeration such as CivicTypes or PlayerTypes to an int without requiring a cast. It's only when going the other way that it makes you manually cast the int using "(CivicTypes)".

BTW, this casting doesn't actually do anything--the compiler makes you do it so that you have to make a conscious decision to treat a random int as a constrained value like CivicTypes. It will merrily allow you to cast an invalid value like -2372 to a PlayerTypes.

Any luck on finding what's wrong with the code? It looks logically fine here. My only guess is that you have the targets backwards, i.e. look up eCivic and check it for getCivicAttitudeReason(eTargetCivic) rather then the other way around.

Also, right now you should see the XML Text key in the hover: TXT_KEY_CIVIC_ATTITUDE_EMANCIPATION_HATES_SLAVERY or whatever you use. You'll need to eventually look up the translated phrase using that key. Right now, however, you just need to get your code detecting the modifiers. Given that the code in CvPlayer::getCivicAttitudeModifier() works, this should as well.

This is my code now:

Code:
    CivicTypes eTargetCivic;
    CivicTypes eCivic;
    for (int iJ = 0; iJ < GC.getNumCivicOptionInfos(); iJ++)
    {
        eTargetCivic = GET_PLAYER(eTargetPlayer).getCivics((CivicOptionTypes)iJ);
        eCivic = GET_PLAYER(ePlayer).getCivics((CivicOptionTypes)iJ);
        if (GC.getCivicInfo(eTargetCivic).getCivicAttitudeChange(eCivic) != 0)
        {
            szBuffer.append(NEWLINE);
            szBuffer.append(GC.getCivicInfo(eCivic).getCivicAttitudeReason(eTargetCivic));
        }
    }

But I still don't see any hover text. That second for loop didn't seem to be needed, so I removed it.

Perhaps my XML is to blame?

This is what I added to the schema:

Code:
    <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>
 
Back
Top Bottom