Troubles with Arrays and Arrays of Arrays...

OK, here is what I came up with. It compiles, but that means nothing now, as I know enough to make it compile, but not enough to ensure it is doing what I need it to do; and as I say I don't understand the XML parsing functions, this is purely based on guesswork:

Please let me know if you see any problems with it.

Stealing the code out of the quote and commenting within it. Will color all comments so they stand out. Also will color the code I think you added so you can tell if I missed something in review.

Code:
void CvXMLLoadUtility::SetVariableListTagPair(int **ppiList, const  TCHAR* szRootTagName,
        int iInfoBaseSize, int iInfoBaseLength[COLOR=MediumTurquoise],
        const TCHAR* szValueTagName, int iValueInfoBaseLength[/COLOR], int  iDefaultListVal)
{
    int i;
    int iIndexVal;
    int iNumSibs;
    TCHAR szTextVal[256];
    int* piList;

    if (0 > iInfoBaseLength)
    {
        char    szMessage[1024];
        sprintf( szMessage, "Allocating zero or less memory in  CvXMLLoadUtility::SetVariableListTagPair \n Current XML file is: %s",  GC.getCurrentXMLFile().GetCString());
        gDLL->MessageBox(szMessage, "XML Error");
    }
    InitList(ppiList, iInfoBaseLength, iDefaultListVal);
    if  (gDLL->getXMLIFace()->SetToChildByTagName(m_pFXml,szRootTagName))
    {
        if (SkipToNextVal())
        {
            iNumSibs =  gDLL->getXMLIFace()->GetNumChildren(m_pFXml);
            piList = *ppiList;
            if (0 < iNumSibs)
            {
                if(!(iNumSibs <= iInfoBaseLength))
                {
                    char    szMessage[1024];
                    sprintf( szMessage, "There are more siblings than  memory allocated for them in CvXMLLoadUtility::SetVariableListTagPair \n  Current XML file is: %s", GC.getCurrentXMLFile().GetCString());
                    gDLL->MessageBox(szMessage, "XML Error");
                }
[COLOR=SandyBrown]//This next line is in the WRONG place.  You need to be inside the NumSibs loop.[/COLOR]
               [COLOR=MediumTurquoise] if  (gDLL->getXMLIFace()->SetToChildByTagName(m_pFXml,szValueTagName))
                {[/COLOR]
                    for (i=0;i<iNumSibs;i++)
                    {
                        if (GetChildXmlVal(szTextVal))
                        {
                            iIndexVal = FindInInfoClass(szTextVal);

                            if (iIndexVal != -1)
                            {
                                GetNextXmlVal(&piList[iIndexVal]);
[COLOR=SandyBrown]//HERE is where you want to add your code, this is what just read the boolean.[/COLOR]
                            }
[COLOR="SandyBrown"]//This is actually sorta gibberish.  You are comparing the BuildingClass enum (iIndexVal) against the number of Techs (iValueInfoBaseLength)[/COLOR]
[COLOR="MediumTurquoise"]                            if( (iIndexVal > iValueInfoBaseLength) ||  (iIndexVal < -1) )
                            {
                                char    szMessage[1024];
                                sprintf( szMessage, "A defined value for  an array is outside of the accepted size of the infoclass!\n Current  XML file is: %s", GC.getCurrentXMLFile().GetCString());
                                gDLL->MessageBox(szMessage, "XML  Error");
                            }
[/COLOR]
                            gDLL->getXMLIFace()->SetToParent(m_pFXml);
                        }

                        if  (!gDLL->getXMLIFace()->NextSibling(m_pFXml))
                        {
                            break;
                        }
                    }

                    gDLL->getXMLIFace()->SetToParent(m_pFXml);
                }
            }
        }

        gDLL->getXMLIFace()->SetToParent(m_pFXml);
    }
}

Also note that while you do set yourself to the new tag (techs) that you are adding and wanting to read, you never do actually read the value of that tag, nor look it up to convert to an INT.
 
Ah, the issue is the function:

GetNextXmlVal

I don't want to do this, I want to drop to the defined tag. Unfortunately I'm not seeing code where I can say "Take the value is this tag szDefinedTag"
 
OK, so digging through XMLLoadUtilityGet I found this function, it looks like it does what i need: GetChildXmlValByName.

So following Xienwolf's advice, here is the new functions, which compiles.

Code:
void CvXMLLoadUtility::SetVariableListTagPair(int **ppiList, const TCHAR* szRootTagName,
		int iInfoBaseSize, int iInfoBaseLength,
		[color="blue"]const TCHAR* szValueTagName, int iValueInfoBaseLength,[/color] int iDefaultListVal)
{
	int i;
	int iIndexVal;
	[color="blue"]int iValue;[/color]
	int iNumSibs;
	TCHAR szTextVal[256];
	int* piList;

	if (0 > iInfoBaseLength)
	{
		char	szMessage[1024];
		sprintf( szMessage, "Allocating zero or less memory in CvXMLLoadUtility::SetVariableListTagPair \n Current XML file is: %s", GC.getCurrentXMLFile().GetCString());
		gDLL->MessageBox(szMessage, "XML Error");
	}
	InitList(ppiList, iInfoBaseLength, iDefaultListVal);
	if (gDLL->getXMLIFace()->SetToChildByTagName(m_pFXml,szRootTagName))
	{
		if (SkipToNextVal())
		{
			iNumSibs = gDLL->getXMLIFace()->GetNumChildren(m_pFXml);
			piList = *ppiList;
			if (0 < iNumSibs)
			{
				if(!(iNumSibs <= iInfoBaseLength))
				{
					char	szMessage[1024];
					sprintf( szMessage, "There are more siblings than memory allocated for them in CvXMLLoadUtility::SetVariableListTagPair \n Current XML file is: %s", GC.getCurrentXMLFile().GetCString());
					gDLL->MessageBox(szMessage, "XML Error");
				}
				if (gDLL->getXMLIFace()->SetToChild(m_pFXml))
				{
					for (i=0;i<iNumSibs;i++)
					{
						if (GetChildXmlVal(szTextVal))
						{
							iIndexVal = FindInInfoClass(szTextVal);

							if (iIndexVal != -1)
							{
								[color="blue"]GetChildXmlValByName(szTextVal, szValueTagName);
								iValue = FindInInfoClass(szTextVal);
								if( (iValue < -1) || (iValue > iValueInfoBaseLength) )
								{
									char	szMessage[1024];
									sprintf( szMessage, "A defined value for an array is outside of the accepted size of the infoclass!\n Current XML file is: %s", GC.getCurrentXMLFile().GetCString());
									gDLL->MessageBox(szMessage, "XML Error");
								}else
								{
									piList[iIndexVal] = iValue;
								}[/color]
							}

							gDLL->getXMLIFace()->SetToParent(m_pFXml);
						}

						if (!gDLL->getXMLIFace()->NextSibling(m_pFXml))
						{
							break;
						}
					}

					gDLL->getXMLIFace()->SetToParent(m_pFXml);
				}
			}
		}

		gDLL->getXMLIFace()->SetToParent(m_pFXml);
	}
}
 
Ugh, dark blue is a bad color for me :) I use Black CFC Skin.

now you are reading in the value you want to read, the TechnologyInfo item. But you have stopped reading the boolean. This means you don't know if the buildingclass is actually required or not.

This is one issue I didn't think of which makes it IMPOSSIBLE to handle as an overloaded function, you absolutely must make a new function to do this, because you need a new return type.

This function returns a single array of integers. So the best you can possibly do with that is what I outlined in my simple example, invent a new "buildingclass not required" definition, like -2, and use the proper enums to mark tech overrides (or lack thereof).

. Now, you could avoid the -2 requirement by insisting that every buildingclass MUST have an override, and using TECH_FUTURE_TECH for those where you don't WANT an override. Then a -1 value means buildingclass not required, and any other value means class is required and tells us which tech overrides it, accepting that once you have Future Tech all building requirements are nullified. But that is a workaround, and I would rather have code which someone can break if they don't understand how to use it than have a workaround personally (especially since that breaking is unlikely).

In order to have the option of not requiring a buildingclass, and the option of having no tech override, you MUST have at least a double array or utilize some sort of workaround, in which case the first idea I posted is the best one IMO.

For the new return type, it would either be a 2d array of size [2][infolength], and you would store the boolean in the first slot and the tech override in the second slot, OR you would define a struct and use an array of that as your return type. The struct would just be a boolean and an int, storing .isRequired() and .iOverrideVal(). The struct is less memory allocated, but you have to learn how to use structs (which isn't a bad thing, they are very useful to understand, and quite easy too)
 
I have 2 functions in CvUnitInfo

bool getPrereqBuildingClass(int i)

int getPrereqBuildingClassTech(int i)

Where the first takes a buildingclass as an argument and says true or false, and the second takes a buildingclass as an argument and passes back an int corresponding to a tech. When the code is set up in CvGameTextManager and CvPlot you simply check the boolean first, before passing in a buildingclass to test if the tech is known or not.

One problem I've having though (assuming I am now loading in CvInfos correctly), is that CvPlot::canTrain does not have the player specified anywhere. I can ask for the owner of the plot, but this means the player could theoretically go into someone elses territory and upgrade their units, if a mod allows upgrading outside a player's territory.
 
So far, you are not loading the XML properly is the point.

If all queries will go through those two functions, then your easiest approach remains the one I outlined. If you want the XML grouped nicely like you demonstrated. In this case though you would ditch the boolean and replace it with the Tech (or NONE).

The next easiest approach is to have 2 sets of arrays in the XML, one for the building booleans, and another for the tech overrides. Fortunately you don't have to worry about keeping them in the same order as one another as the building class defines where in the array the data is stored. For this approach you can still just use the default SetTagListPair. It avoids use of a -2, so if someone fails to check your boolean query and skips to the Tech check they cannot break the code by looking up a -2 enum.

But if you want to load the XML in EXACTLY the way that you first posted, boolean + Tech, you must write a completely new function with a different return type, passing back a multi-dimensional array, or an array of structs.


One other alternative occurs to me I suppose. You could use my method for loading the XML (with the -2 and techs instead of bools), this array would be your m_piPrereqBuildingClassTech array, your m_pbPrereqBuildingClass array would be empty at this point. You then define m_pbPrereqBuildingClass and initialize it with all 0 values. Loop over NumBuildingClasses and read m_piPrereqBuildingClassTech. If there is a -2, then that element in the boolean remains 0, and you replace that element in m_piPrereqBuildingClassTech with a -1. If that element is -1 or higher in m_piPrereqBuildingClassTech, then you set the corresponding boolean to 1.

This would get you 2 arrays in the code, it would be impossible to accidentally pick up a -2 for an enum check, and the XML would look nice and streamlined. No need to write anything new and learn how to read from XML or construct a Struct for yourself.

I am willing to walk you through the more complicated method so you can understand how to read XML and build structs, but you have to outline how you want to leave the functions flexible for future use, as there are quite likely dozens of ways to approach it, and I'd rather not discover that you would have been better served by a different setup when we are nearly done :) But I honestly don't think that it is required to make things more complicated at this point.
 
Ugh, dark blue is a bad color for me :) I use Black CFC Skin.

now you are reading in the value you want to read, the TechnologyInfo item. But you have stopped reading the boolean. This means you don't know if the buildingclass is actually required or not.

I suppose I wasn't clear on this; but I have left the original boolean array intact, it is unaltered, and since it uses the game's normal SetVariableListTagPair function, it will drop to the next tag (the boolean value), and assign that for the buildingclass boolean array. The tech int array is a completely different function. By overloading the function I meant I overloaded SetVariableListTagPair, so that it could be directed to load the value from a specified tag, that's the code I'm showing above. I am just assuming at this point that any future coder will do a boolean test first, before they check the prereq tech; anyway I think that it's necessary to have a nested check anyway you do it, so it doesn't seem like it requires any extra work.

I haven't had a chance to test the code yet; as it takes 2 hours to compile a new gamecore on my comp, and this has obviously taken a while to code, and I can't be home writing code for Civ4 all day; especially today since it's cinco de Mayo, and it's getting close to time to take taquilla shots. And writing code drunk has never gone well for me. But logically I dont' see any problems with this so long as the int array is loading properly; and I can't see how having two separate functions that look at two different tags to assign their values wouldn't work.
 
The solution with two separate xml arrays would be done already. imjussayin.

:lol: The two-array solution was already complete in my post.

Another issue with the single array solution is that you can't extend it to have more than one extra value. Say you wanted to allow for an override tech and an override bonus.

Also, I don't quite understand the benefit of passing in the XML tag to be read instead of just reading the next tag. It's not like you can call the same function with two separate tags to read in three-valued elements. I do agree that code that doesn't depend on the order of elements is good in theory, but in practice here it's pointless since you cannot take advantage of it. I must be missing something and would love to know what it is.

Edit: Ah, I suppose you can navigate back up to the parent (can you with this XML API?) and then process the list again, reading in a different tag. Is that your goal?
 
Ok, if you are still loading the booleans an a separate line during CvInfos then you are probably good to go at this point.

EF: You could re-navigate to the same parent tag and have it look up a new nested child. That approach actually sounds like a fairly robust and easily coded alternative approach to handling multiple nested tags.

Code:
pXML->SetVariableListTagPair(&m_piFirstElement, "ReadingStuffs", sizeof(GC.getStuffInfo((StuffTypes)0), GC.getNumStuffInfos(), "First", GC.getNumOtherInfos());
pXML->SetVariableListTagPair(&m_piSecondElement, "ReadingStuffs", sizeof(GC.getStuffInfo((StuffTypes)0), GC.getNumStuffInfos(), "Second", GC.getNumOtherInfos());

This would form m_piFirstElement & m_piSecondElement out of the XML container of:

Code:
<ReadingStuffs>
    <ReadingStuff>
        <StuffType>STUFF_FOO</StuffType>
        <First>FIRST_SOMETHING</First>
        <Second>SECOND_OTHER</Second>
    </ReadingStuff>
    <ReadingStuff>
        <StuffType>STUFF_BAR</StuffType>
        <First>FIRST_ANYTHING</First>
        <Second>SECOND_NOTHING</Second>
    </ReadingStuff>
</ReadingStuffs>
 
Unfortunately the function GetChildXmlValByName is not working correctly. Checking the debugger all I keep getting is the default value, -1, and it's not pulling the actual string from the tag I'm telling it to look up.

Here is the code in question:

Code:
if (iIndexVal != -1)
{
	GetChildXmlValByName(szTextVal, szValueTagName);
	int debugValue = FindInInfoClass(szTextVal);
	piList[iIndexVal] = FindInInfoClass(szTextVal);
szValueTagName is "TechOverride" as it should be where I am setting the break point and examining it. After the function runs though szTextVal is just garbage, and when findInfoClass checks it, it returns -1; it should be pulling TECH_IRON_WORKING where I am having it break, but that does not occur; instead the value BUILDINGCLASS_BARRACKS that's originally stored in szTextVal (this variable originally stores the array position, and thus is still holding this value) is getting assigned garbage (a large and nonsensical hex value) after GetChildXmlValByName is called. Somehow FindInfoClass decides this garbage corresponds to -1, instead of spitting out garbage of it's own.

I'm very confused because here is another example of GetChildXmlValByName being used:
Code:
for (i=0;i<iNumSibs;i++)
{
	GetChildXmlValByName(szTextVal, "FeatureType");
	iFeatureIndex = FindInInfoClass(szTextVal);
	if(!(iFeatureIndex != -1))
	{
		char	szMessage[1024];
		sprintf( szMessage, "iFeatureIndex is -1 inside SetFeatureStruct function \n Current XML file is: %s", GC.getCurrentXMLFile().GetCString());
		gDLL->MessageBox(szMessage, "XML Error");
	}
	GetChildXmlValByName(szTextVal, "PrereqTech");
	paiFeatureTech[iFeatureIndex] = FindInInfoClass(szTextVal);

I can see no difference in how I am calling it, and how it is being called there. :dunno:

For full reference here are the two full functions (mine, and the featureinfos reference):
Spoiler :
Code:
void CvXMLLoadUtility::SetVariableListTagPair(int **ppiList, const TCHAR* szRootTagName,
		int iInfoBaseSize, int iInfoBaseLength,
		const TCHAR* szValueTagName, int iValueInfoBaseLength, int iDefaultListVal)
{
	int i;
	int iIndexVal;
	int iNumSibs;
	TCHAR szTextVal[256];
	int* piList = NULL;

	if (0 > iInfoBaseLength)
	{
		char	szMessage[1024];
		sprintf( szMessage, "Allocating zero or less memory in CvXMLLoadUtility::SetVariableListTagPair \n Current XML file is: %s", GC.getCurrentXMLFile().GetCString());
		gDLL->MessageBox(szMessage, "XML Error");
	}
	InitList(ppiList, iInfoBaseLength, iDefaultListVal);
	piList = *ppiList;

	if (gDLL->getXMLIFace()->SetToChildByTagName(m_pFXml,szRootTagName))
	{
		if (SkipToNextVal())
		{
			iNumSibs = gDLL->getXMLIFace()->GetNumChildren(m_pFXml);
			if (0 < iNumSibs)
			{
				if(!(iNumSibs <= iInfoBaseLength))
				{
					char	szMessage[1024];
					sprintf( szMessage, "There are more siblings than memory allocated for them in CvXMLLoadUtility::SetVariableListTagPair \n Current XML file is: %s", GC.getCurrentXMLFile().GetCString());
					gDLL->MessageBox(szMessage, "XML Error");
				}
				if (gDLL->getXMLIFace()->SetToChild(m_pFXml))
				{
					for (i=0;i<iNumSibs;i++)
					{
						if (GetChildXmlVal(szTextVal))
						{
							iIndexVal = FindInInfoClass(szTextVal);

							if (iIndexVal != -1)
							{
								GetChildXmlValByName(szTextVal, szValueTagName);
								int debugValue = FindInInfoClass(szTextVal);
								piList[iIndexVal] = FindInInfoClass(szTextVal);
								if( (FindInInfoClass(szTextVal) < -1) || (FindInInfoClass(szTextVal) > iValueInfoBaseLength) )
								{
									char	szMessage[1024];
									sprintf( szMessage, "A defined value for an array is outside of the accepted size of the infoclass!\n Current XML file is: %s", GC.getCurrentXMLFile().GetCString());
									gDLL->MessageBox(szMessage, "XML Error");
								}
							}

							gDLL->getXMLIFace()->SetToParent(m_pFXml);
						}

						if (!gDLL->getXMLIFace()->NextSibling(m_pFXml))
						{
							break;
						}
					}

					gDLL->getXMLIFace()->SetToParent(m_pFXml);
				}
			}
		}

		gDLL->getXMLIFace()->SetToParent(m_pFXml);
	}
}
Code:
void CvXMLLoadUtility::SetFeatureStruct(int** ppiFeatureTech, int** ppiFeatureTime, int** ppiFeatureProduction, bool** ppbFeatureRemove)
{
	int i=0;				//loop counter
	int iNumSibs;					// the number of siblings the current xml node has
	int iFeatureIndex;
	TCHAR szTextVal[256];	// temporarily hold the text value of the current xml node
	int* paiFeatureTech = NULL;
	int* paiFeatureTime = NULL;
	int* paiFeatureProduction = NULL;
	bool* pabFeatureRemove = NULL;

	if(GC.getNumFeatureInfos() < 1)
	{
		char	szMessage[1024];
		sprintf( szMessage, "no feature infos set yet! \n Current XML file is: %s", GC.getCurrentXMLFile().GetCString());
		gDLL->MessageBox(szMessage, "XML Error");
	}
	InitList(ppiFeatureTech, GC.getNumFeatureInfos(), -1);
	InitList(ppiFeatureTime, GC.getNumFeatureInfos());
	InitList(ppiFeatureProduction, GC.getNumFeatureInfos());
	InitList(ppbFeatureRemove, GC.getNumFeatureInfos());

	paiFeatureTech = *ppiFeatureTech;
	paiFeatureTime = *ppiFeatureTime;
	paiFeatureProduction = *ppiFeatureProduction;
	pabFeatureRemove = *ppbFeatureRemove;

	if (gDLL->getXMLIFace()->SetToChildByTagName(m_pFXml,"FeatureStructs"))
	{
		iNumSibs = gDLL->getXMLIFace()->GetNumChildren(m_pFXml);

		if (0 < iNumSibs)
		{
			if (gDLL->getXMLIFace()->SetToChildByTagName(m_pFXml,"FeatureStruct"))
			{
				if(!(iNumSibs <= GC.getNumFeatureInfos()))
				{
					char	szMessage[1024];
					sprintf( szMessage, "iNumSibs is greater than GC.getNumFeatureInfos in SetFeatureStruct function \n Current XML file is: %s", GC.getCurrentXMLFile().GetCString());
					gDLL->MessageBox(szMessage, "XML Error");
				}
				for (i=0;i<iNumSibs;i++)
				{
					GetChildXmlValByName(szTextVal, "FeatureType");
					iFeatureIndex = FindInInfoClass(szTextVal);
					if(!(iFeatureIndex != -1))
					{
						char	szMessage[1024];
						sprintf( szMessage, "iFeatureIndex is -1 inside SetFeatureStruct function \n Current XML file is: %s", GC.getCurrentXMLFile().GetCString());
						gDLL->MessageBox(szMessage, "XML Error");
					}
					GetChildXmlValByName(szTextVal, "PrereqTech");
					paiFeatureTech[iFeatureIndex] = FindInInfoClass(szTextVal);
					GetChildXmlValByName(&paiFeatureTime[iFeatureIndex], "iTime");
					GetChildXmlValByName(&paiFeatureProduction[iFeatureIndex], "iProduction");
					GetChildXmlValByName(&pabFeatureRemove[iFeatureIndex], "bRemove");

					if (!gDLL->getXMLIFace()->NextSibling(m_pFXml))
					{
						break;
					}
				}

				gDLL->getXMLIFace()->SetToParent(m_pFXml);
			}
		}

		gDLL->getXMLIFace()->SetToParent(m_pFXml);
	}
}
Code:
<PrereqBuildingClasses>
	<PrereqBuildingClass>
		<BuildingClassType>BUILDINGCLASS_BARRACKS</BuildingClassType>
		<bPrereq>1</bPrereq>
		<TechOverride>TECH_IRON_WORKING</TechOverride>
		<EraOverride>NONE</EraOverride>
	</PrereqBuildingClass>
</PrereqBuildingClasses>
 
The problem is that SetToChildByTagName looks inside the XML one step deeper. It wants the XML to be like this:

Code:
<PrereqBuildingClasses>
	<PrereqBuildingClass>
		<BuildingClassType>BUILDINGCLASS_BARRACKS</BuildingClassType>
		<bPrereq>1</bPrereq>
		<Overrides>
			<TechOverride>TECH_IRON_WORKING</TechOverride>
			<EraOverride>NONE</EraOverride>
		</Overrides>
		</PrereqBuildingClass>
</PrereqBuildingClasses>

Which I suppose I can do, it seems like meaningless extra steps though, but it looks like this must be done to get it to work.

I'm also a bit confused about how to set up the schema to deal with this, but I'll try tinkering with it and see if I can't get something to work.

Edit:
Yeah, I don't understand the schema here, how it would want to be set this up; there aren't really any examples, as there are no tags loaded like this (hence the problem).

If I have XML like this:
Code:
			<PrereqBuildingClasses>
				<PrereqBuildingClass>
					<BuildingClassType>BUILDINGCLASS_BARRACKS</BuildingClassType>
					<bPrereq>1</bPrereq>
					<Overrides>
						<TechOverride>TECH_IRON_WORKING</TechOverride>
						<EraOverride>NONE</EraOverride>
					</Overrides>
				</PrereqBuildingClass>
			</PrereqBuildingClasses>

What's the schema supposed to look like? I tried this, but it fails:
Code:
	<ElementType name="BuildingClassType" content="textOnly"/>
	<ElementType name="bPrereq" content="textOnly" dt:type="boolean"/>
	<ElementType name="Overrides" content="eltOnly">
		<element type="TechOverride" minOccurs="0" maxOccurs="*"/>
		<element type="EraOverride" minOccurs="0" maxOccurs="*"/>
	</ElementType>
	<ElementType name="PrereqBuildingClass" content="eltOnly">
		<element type="BuildingClassType"/>
		<element type="bPrereq" minOccurs="0" maxOccurs="*"/>
		<element type="Overrides" minOccurs="0" maxOccurs="*"/>
	</ElementType>
	<ElementType name="PrereqBuildingClasses" content="eltOnly">
		<element type="PrereqBuildingClass" minOccurs="0" maxOccurs="*"/>
	</ElementType>
 
Is there a GetSiblingXmlValByName() function? I think the problem is that the parser is currently pointing after the <bPrereq> element since it was just read. Or you could call SetToParent() before calling GetChildXmlValByName().
 
Or you could call SetToParent() before calling GetChildXmlValByName().

It now correctly get's the value, but it is not applying it. I think I must have something wrong with the Pointers for the list. I don't see anything wrong though, and when I step through the code in the debugger everything looks right, it's just when I go in game, there is obviously no value stored in the array that calls for the tech override.

Edit:

For clarity, I set up a breakpoint at the beggining of this code. It trips four times (two units have a prereq buildingclass set, so this is what I expect, as it should run for both era override, and tech override):
Code:
if (iIndexVal != -1)
{
	gDLL->getXMLIFace()->SetToParent(m_pFXml);
	GetChildXmlValByName(szTextVal, szValueTagName);
	iValue = FindInInfoClass(szTextVal);
	piList[iIndexVal] = iValue;
Stepping through the debugger, I am getting the correct values in all the spots (szValueTagName is "TechOverride", iValue returns 71, and szTextVal is "TECH_IRON_WORKING", indexVal is 5, and corresponds to BUILDINGCLASS_BARRACKS which is stored in szTextPosition). Everything is correct, but for some reason this value of 71 (the ironworking tech position), isn't being stored in the array.

UnitInfos calls this function here:
Code:
pXML->SetVariableListTagPair(&m_piPrereqBuildingClassOverrideTech, "PrereqBuildingClasses", sizeof(GC.getBuildingClassInfo((BuildingClassTypes)0)), GC.getNumBuildingClassInfos(),
		"TechOverride", GC.getNumTechInfos());
And I've double and tripplechecked everything in CvInfos, it all is correct. My hunch is that this line isn't working correctly, and is not assigning the value correctly to the array:
piList[iIndexVal] = iValue;
But I've looked a lot through CvXmlLoadUtilitySet quite a bit, and it seems like this should be correct. The whole function is this:
Spoiler :
Code:
void CvXMLLoadUtility::SetVariableListTagPair(int **ppiList, const TCHAR* szRootTagName,
		int iInfoBaseSize, int iInfoBaseLength,
		const TCHAR* szValueTagName, int iValueInfoBaseLength, int iDefaultListVal)
{
	int i;
	int iIndexVal;
	int iNumSibs;
	int iValue;
	TCHAR szTextPosition[256];
	TCHAR szTextVal[256];
	int* piList = NULL;

	if (0 > iInfoBaseLength)
	{
		char	szMessage[1024];
		sprintf( szMessage, "Allocating zero or less memory in CvXMLLoadUtility::SetVariableListTagPair \n Current XML file is: %s", GC.getCurrentXMLFile().GetCString());
		gDLL->MessageBox(szMessage, "XML Error");
	}
	InitList(ppiList, iInfoBaseLength, iDefaultListVal);
	piList = *ppiList;

	if (gDLL->getXMLIFace()->SetToChildByTagName(m_pFXml,szRootTagName))
	{
		if (SkipToNextVal())
		{
			iNumSibs = gDLL->getXMLIFace()->GetNumChildren(m_pFXml);
			if (0 < iNumSibs)
			{
				if(!(iNumSibs <= iInfoBaseLength))
				{
					char	szMessage[1024];
					sprintf( szMessage, "There are more siblings than memory allocated for them in CvXMLLoadUtility::SetVariableListTagPair \n Current XML file is: %s", GC.getCurrentXMLFile().GetCString());
					gDLL->MessageBox(szMessage, "XML Error");
				}
				if (gDLL->getXMLIFace()->SetToChild(m_pFXml))
				{
					for (i=0;i<iNumSibs;i++)
					{
						if (GetChildXmlVal(szTextPosition))
						{
							iIndexVal = FindInInfoClass(szTextPosition);

							if (iIndexVal != -1)
							{
								gDLL->getXMLIFace()->SetToParent(m_pFXml);
								GetChildXmlValByName(szTextVal, szValueTagName);
								iValue = FindInInfoClass(szTextVal);
								piList[iIndexVal] = iValue;
								if( (iValue < -1) || (iValue > iValueInfoBaseLength) )
								{
									char	szMessage[1024];
									sprintf( szMessage, "A defined value for an array is outside of the accepted size of the infoclass!\n Current XML file is: %s", GC.getCurrentXMLFile().GetCString());
									gDLL->MessageBox(szMessage, "XML Error");
								}
							}
						}

						if (!gDLL->getXMLIFace()->NextSibling(m_pFXml))
						{
							break;
						}
					}

					gDLL->getXMLIFace()->SetToParent(m_pFXml);
				}
			}
		}

		gDLL->getXMLIFace()->SetToParent(m_pFXml);
	}
}

Anyone have any ideas as to what could be going wrong? Seems really close, and I thought I'd have it once it finally read the tag...
 
It looks like piList is actually a pointer to the array, so you need to dereference it first:

Code:
[B][COLOR="Red"](*piList)[/COLOR][/B][iIndexVal] = iValue;

You might want to rename it ppiList to reflect this.

Edit: Nevermind. I see that you have declared a second piList that holds the dereferenced ppiList argument.
 
Your new design of how you think the XML should work doesn't feel right to me at all. Here is a code snippet where I had written custom XML reading steps to pick up my own nested XML family:

Code:
	if (gDLL->getXMLIFace()->SetToChildByTagName(pXML->GetXML(),"CityBonuses"))
	{
		int iNumSibs = gDLL->getXMLIFace()->GetNumChildren(pXML->GetXML());
		if (iNumSibs > 0)
        {
			m_iNumCityBonuses = iNumSibs;
			if (gDLL->getXMLIFace()->SetToChildByTagName(pXML->GetXML(),"CityBonus"))
            {
				for (int iI = 0; iI < iNumSibs; iI++)
				{
					CityBonuses cbTemp;
                    pXML->GetChildXmlValByName(&(cbTemp.bFullMap), "bFullMap", false);
                    pXML->GetChildXmlValByName(&(cbTemp.bApplyEnemy), "bApplyEnemy", false);
					pXML->GetChildXmlValByName(&(cbTemp.bApplyRival), "bApplyRival", false);
					pXML->GetChildXmlValByName(&(cbTemp.bApplySelf), "bApplySelf", false);
					pXML->GetChildXmlValByName(&(cbTemp.bApplyTeam), "bApplyTeam", false);
					pXML->GetChildXmlValByName(&(cbTemp.fCulture), "fCulture", 0.0f);
					pXML->GetChildXmlValByName(&(cbTemp.fDefense), "fDefense", 0.0f);
					pXML->GetChildXmlValByName(&(cbTemp.fDiplo), "fDiplo", 0.0f);
					pXML->GetChildXmlValByName(&(cbTemp.fFood), "fFood", 0.0f);
					pXML->GetChildXmlValByName(&(cbTemp.fFreeXP), "fFreeXP", 0.0f);
					pXML->GetChildXmlValByName(&(cbTemp.fGold), "fGold", 0.0f);
					pXML->GetChildXmlValByName(&(cbTemp.fGPP), "fGPP", 0.0f);
					pXML->GetChildXmlValByName(&(cbTemp.fHappy), "fHappy", 0.0f);
					pXML->GetChildXmlValByName(&(cbTemp.fHealth), "fHealth", 0.0f);
					pXML->GetChildXmlValByName(&(cbTemp.fProduction), "fProduction", 0.0f);
					pXML->GetChildXmlValByName(&(cbTemp.fTradeRoutes), "fTradeRoutes", 0.0f);
					pXML->GetChildXmlValByName(&(cbTemp.fRitualAssist), "fRitualAssist", 0.0f);
					pXML->GetChildXmlValByName(&(cbTemp.fInfectCulture), "fInfectCulture", 0.0f);
					pXML->GetChildXmlValByName(&(cbTemp.fPotency), "fPotency", 0.0f);
					pXML->GetChildXmlValByName(&(cbTemp.fShielding), "fShielding", 0.0f);
					pXML->GetChildXmlValByName(&(cbTemp.fTrainXPCap), "fTrainXPCap", 0.0f);
					pXML->GetChildXmlValByName(&(cbTemp.fTrainXPRate), "fTrainXPRate", 0.0f);
					pXML->GetChildXmlValByName(&(cbTemp.iBonusRange), "iBonusRange", 0);
					pXML->GetChildXmlValByName(&(cbTemp.fDecayRate), "fDecayRate", 0.0f);
					m_cbCityBonuses.push_back(cbTemp);
					if (!gDLL->getXMLIFace()->NextSibling(pXML->GetXML()))						break;
				}
				gDLL->getXMLIFace()->SetToParent(pXML->GetXML());
			}
		}
		gDLL->getXMLIFace()->SetToParent(pXML->GetXML());
	}

This is capable of reading in the XML:
Code:
			<CityBonuses>
				<CityBonus>
					<bApplyEnemy>0</bApplyEnemy>
					<bApplyRival>1</bApplyRival>
					<bApplySelf>1</bApplySelf>
					<bApplyTeam>0</bApplyTeam>
					<bFullMap>0</bFullMap>
					<fCulture>0</fCulture>
					<fDefense>0</fDefense>
					<fDiplo>0</fDiplo>
					<fFood>0</fFood>
					<fFreeXP>0</fFreeXP>
					<fGold>0</fGold>
					<fGPP>0</fGPP>
					<fHappy>0</fHappy>
					<fHealth>0</fHealth>
					<fInfectCulture>0</fInfectCulture>
					<fPotency>0</fPotency>
					<fProduction>0</fProduction>
					<fRitualAssist>50</fRitualAssist>
					<fShielding>0</fShielding>
					<fTradeRoutes>0</fTradeRoutes>
					<fTrainXPCap>20</fTrainXPCap>
					<fTrainXPRate>40</fTrainXPRate>
					<iBonusRange>0</iBonusRange>
					<fDecayRate>0</fDecayRate>
				</CityBonus>
				<CityBonus>
					<bApplyEnemy>1</bApplyEnemy>
					<bFullMap>1</bFullMap>
					<fHappy>-5</fHappy>
				</CityBonus>
				<CityBonus>
					<bApplyEnemy>1</bApplyEnemy>
					<bFullMap>1</bFullMap>
					<fDiplo>-5</fDiplo>
				</CityBonus>
			</CityBonuses>

As you can see, it doesn't nest itself 1 deeper when using the GetChildXmlValByName command.

But it seems you are possibly past that point, so nevermind if so.


Ok, for your loading into the array, we'll examine piece by piece.

void CvXMLLoadUtility::SetVariableListTagPair(int **ppiList, const TCHAR* szRootTagName,
int iInfoBaseSize, int iInfoBaseLength,
const TCHAR* szValueTagName, int iValueInfoBaseLength, int iDefaultListVal)

The function receives ppiList (m_piPrereqBuildingClassOverrideTech), with permission to change the contents.

int* piList = NULL;
We make a local array to manipulate, piList.

InitList(ppiList, iInfoBaseLength, iDefaultListVal);
We define ppiList (m_piPrereqBuildingClassOverrideTech) to be size GC.getNumBuildingClassInfos()

piList = *ppiList;
We give piList and ppiList (m_piPrereqBuildingClassOverrideTech) the same location in memory, thus any changes to one change the other.

if (GetChildXmlVal(szTextPosition))
{
iIndexVal = FindInInfoClass(szTextPosition);

We check what BuildingClass is listed in the current child, using that to determine where in the array (of BuildingClass size) we will write our information.

gDLL->getXMLIFace()->SetToParent(m_pFXml);
GetChildXmlValByName(szTextVal, szValueTagName);

Still don't think you should be doing it this way, but if it works it works.
Notice the lack of & on szTextVal. You aren't granting permission for the value stored in szTextVal to be changed. I think that is your error.

iValue = FindInInfoClass(szTextVal);
So in the debugger when you run to THIS line, and mouse-over szTextVal it has the new Text Value listed within it? If so, then that is very odd, as the prior line should have failed to alter it. Remember that when the debugger is on a line it is ABOUT to run that line, you have to be on a line later in the code for the values to have completed changing (thus at this point and no point earlier should you be able to check szTextVal to see what was read in to it. And not until next line will you be able to see what is assigned to iValue)

piList[iIndexVal] = iValue;
Here we assign a value to a position in piList, and thus in ppiList (m_piPrereqBuildingClassOverrideTech), position in array based on the BuildingClass and value stored based on the TechOverride.

if( (iValue < -1) || (iValue > iValueInfoBaseLength) )
{
char szMessage[1024];
sprintf( szMessage, "A defined value for an array is outside of the accepted size of the infoclass!\n Current XML file is: %s", GC.getCurrentXMLFile().GetCString());
gDLL->MessageBox(szMessage, "XML Error");
}


Really the actually assignment of a value in the array should be done in an ELSE case after this IF check so you don't assign a bogus value. Currently you'd happily assign -10, and then happen to toss out an assert afterward. Someone who ignores the assert continues to play the game with a potential crash looming in the code.
 
void CvXMLLoadUtility::SetVariableListTagPair(int **ppiList, const TCHAR* szRootTagName,
int iInfoBaseSize, int iInfoBaseLength,
const TCHAR* szValueTagName, int iValueInfoBaseLength, int iDefaultListVal)

The function receives ppiList (m_piPrereqBuildingClassOverrideTech), with permission to change the contents.
It's a local character array, created in this function. Just for kicks I tried adding an &, but it causes a compiling error.
int* piList = NULL;
We make a local array to manipulate, piList.

InitList(ppiList, iInfoBaseLength, iDefaultListVal);
We define ppiList (m_piPrereqBuildingClassOverrideTech) to be size GC.getNumBuildingClassInfos()

piList = *ppiList;
We give piList and ppiList (m_piPrereqBuildingClassOverrideTech) the same location in memory, thus any changes to one change the other.

if (GetChildXmlVal(szTextPosition))
{
iIndexVal = FindInInfoClass(szTextPosition);

We check what BuildingClass is listed in the current child, using that to determine where in the array (of BuildingClass size) we will write our information.

gDLL->getXMLIFace()->SetToParent(m_pFXml);
GetChildXmlValByName(szTextVal, szValueTagName);

Still don't think you should be doing it this way, but if it works it works.
Notice the lack of & on szTextVal. You aren't granting permission for the value stored in szTextVal to be changed. I think that is your error.
It seems odd, and I looked through alot of CvXmlLoadUtilitySet and I agree, it doesn't look like this. But not doing so caused "" to be stored in the variable. Now I have TECH_IRON_WORKING, so this is working.

iValue = FindInInfoClass(szTextVal);
So in the debugger when you run to THIS line, and mouse-over szTextVal it has the new Text Value listed within it? If so, then that is very odd, as the prior line should have failed to alter it. Remember that when the debugger is on a line it is ABOUT to run that line, you have to be on a line later in the code for the values to have completed changing (thus at this point and no point earlier should you be able to check szTextVal to see what was read in to it. And not until next line will you be able to see what is assigned to iValue)
All variables have exactly what they should have stored in them when I get to the assignment line. That's what's making this so frustrating now, as before I at least could see exactly what was wrong (ironworking was never read into the variable), now everything has the correct value. I even counted out the BuldingClass position for barracks in BuildingClassInfos, and the index value of 5 is correct for barracks, it is the 6th buildingclass in that file.

piList[iIndexVal] = iValue;
Here we assign a value to a position in piList, and thus in ppiList (m_piPrereqBuildingClassOverrideTech), position in array based on the BuildingClass and value stored based on the TechOverride.

if( (iValue < -1) || (iValue > iValueInfoBaseLength) )
{
char szMessage[1024];
sprintf( szMessage, "A defined value for an array is outside of the accepted size of the infoclass!\n Current XML file is: %s", GC.getCurrentXMLFile().GetCString());
gDLL->MessageBox(szMessage, "XML Error");
}


Really the actually assignment of a value in the array should be done in an ELSE case after this IF check so you don't assign a bogus value. Currently you'd happily assign -10, and then happen to toss out an assert afterward. Someone who ignores the assert continues to play the game with a potential crash looming in the code.
I intend on switching the error yes, but earlier I wasn't getting the tag read at all, so moved it to the end for debugging, just wanted to get everything out of the way so that I could watch the code I was interested in running. This is why I am using multiple variables when one would do (such as szTextValue and szTextVal), as I wanted to be able to see exactly what was in everything. It's all correct, the issue is that the assignment line just isn't popping in 71 at the 5th position of the array. And I cannot figure out why or how this is so....

Edit: I plugged in this line:
int debugValue = ppiList[iIndexVal];

And the debugger shows 71, so I don't get it.

This is clearly showing the array has this value being set in there, but it's not returning the correct value when checked later.

Niether the civilopdia, nor the implementation code is working, and I'm pretty sure it's because 71 is not in that position in the array, I can't see any other explenation. Here is the implementation code though:
Spoiler :
Code:
bool CvPlayer::isBuildingClassRequiredToTrain(BuildingClassTypes eBuildingClass, UnitTypes eUnit) const
{
	CvUnitInfo& kUnit = GC.getUnitInfo(eUnit);
	int iI;
	bool bTechOverride = true;
	bool bEraOverride = true;

	if(kUnit.getPrereqBuildingClass(eBuildingClass))
	{
		for(iI = 0; iI < GC.getNumTechInfos(); iI++)
		{
			if( kUnit.getPrereqBuildingClassOverrideTech(iI) < 0 )
			{
				bTechOverride = false;
				break;
			} else if(!((GET_TEAM(getTeam())).isHasTech(TechTypes(iI))))
			{
				bTechOverride = false;
				break;
			}
		}
		for(iI = 0; iI < GC.getNumEraInfos(); iI++)
		{
			if( (kUnit.getPrereqBuildingClassOverrideEra(iI)) < 0)
			{
				bEraOverride = false;
				break;
			} else if( getCurrentEra() < kUnit.getPrereqBuildingClassOverrideEra(iI) )
			{
				bEraOverride = false;
				break;
			}
		}
		if( (!(bEraOverride) && !(bTechOverride)) )
		{
			return true;
		}
	}
	return false;
}
Code:
bool CvPlot::canTrain()
{
...
		for (iI = 0; iI < GC.getNumBuildingClassInfos(); iI++)
		{
			if (kUnit.getPrereqBuildingClass(iI))
			{
				if((GET_PLAYER(getOwnerINLINE())).isBuildingClassRequiredToTrain(BuildingClassTypes(iI), eUnit))
				{
					if (!isCity() || (!(getPlotCity()->getNumBuilding((BuildingTypes)GC.getCivilizationInfo(getPlotCity()->getCivilizationType()).getCivilizationBuildings(iI)))) )
					{
						return false;
					}
				}
			}
		}

So implemenation and civilopdia entries show nothing, here is the civilopedia code just for kicks as well:
Spoiler :
Code:
class SevoPediaUnit:
...
	def placeRequires(self):
...		if (gc.getGame().getActivePlayer() >= 0):
			pCiv = gc.getCivilizationInfo(gc.getActivePlayer().getCivilizationType())
		else:
			pCiv = None
		for j in range(gc.getNumBuildingClassInfos()):
			if (gc.getUnitInfo(self.iUnit).getPrereqBuildingClass(j)):
				if (pCiv):
					iPrereq = pCiv.getCivilizationBuildings(j)
				else:
					iPrereq = gc.getBuildingClassInfo(j).getDefaultBuildingIndex()
				bTechOverride = False
				bEraOverride = False
				for k in range(gc.getNumTechInfos()):
					if(k == gc.getUnitInfo(self.iUnit).getPrereqBuildingClassOverrideTech(j)):
						iTech = k
						bTechOverride = True
					break
				for k in range(gc.getNumEraInfos()):
					if(k == gc.getUnitInfo(self.iUnit).getPrereqBuildingClassOverrideEra(j)):
						iEra = k
						bEraOverride = True
					break
				screen.attachImageButton(panelName, "", gc.getBuildingInfo(iPrereq).getButton(), GenericButtonSizes.BUTTON_SIZE_CUSTOM, WidgetTypes.WIDGET_PEDIA_JUMP_TO_BUILDING, iPrereq, -1, False)
				if bTechOverride:
					screen.attachLabel(panelName, "", localText.getText("TXT_KEY_UNTIL", ()))
					screen.attachLabel(panelName, "", localText.getText(gc.getTechInfo(iTech).getDescription(), ()))
				if bEraOverride:
					screen.attachLabel(panelName, "", localText.getText("TXT_KEY_UNTIL", ()))
					screen.attachLabel(panelName, "", localText.getText(gc.getEraInfo(iEra).getDescription(), ()))
I mean it's certainly possible I've messed up both the implemenation, and the civilopdia code; but I kind of doubt it. I really think the value just isn't sticking in the array for some reason. And I have no idea what I should do next.
 
Unrelated error: change this

Code:
iValue > iValueInfoBaseLength

to this

Code:
iValue [B][COLOR="Red"]>=[/COLOR][/B] iValueInfoBaseLength

How are you testing that the value is not being stored in the array?
 
I am checking the civilopedia and the functionality, using the code above. It's possible I have made an implementation error with the civilopdia and implementation code, but it's pretty simple (it's all in the above post), and it looks right to me. If you know of a better way, let me know, but I think the civilopedia code is pretty straight forward really, and it is showing the Barracks requirement just fine (just not displaying the Unit tech description override info).
 
Back
Top Bottom