Allocating Zero or Less Memory in CvXMLLoadUtility...

The actual XML would look like:

Code:
<PrereqCivicORs>
  <CivicType>CIVIC_THREE</CivicType>
  <CivicType>CIVIC_FOUR</CivicType>
</PrereqCivicORs>
<PrereqCivicANDs>
  <CivicType>CIVIC_TWO</CivicType>
</PrereqCivicANDs>

So it isn't completely arbitrary, but more flexible than is easily hoisted into another file.


Haven't looked at the code yet, will get a chance to in the next hour or so
 
Pretty simple error in your code:

Code:
	pXML->SetVariableListTagPair(&m_pbPrereqOrCivics, "PrereqOrCivics", sizeof(GC.getCivicInfo((CivicTypes)0)), GC.getNumCivicInfos());

You cannot do this. Remember, we need readpass3 because there are no civics yet, so GC.getNumCivicInfos() returns 0 and you create an empty array. That is where my extra functions in CvXMLLoadUtility come into play, which you don't seem to have yet. With the CvXMLLoadUtility::SetStringWithChildList you can load the actual text in the XML and hold it till readpass3.

In CvPromotionInfo::read(CvXMLLoadUtility* pXML)
Code:
    if (gDLL->getXMLIFace()->SetToChildByTagName(pXML->GetXML(),"PrereqCivilizations"))
	{
		pXML->SetStringWithChildList(&m_iNumPrereqCivilizations, &m_aszPrereqCivilizationsforPass3);
	}

In CvPromotionInfo::readPass3()
Code:
    m_pbPrereqCivilizations = new bool[GC.getNumCivilizationInfos()];
	for(int iI = 0; iI < GC.getNumCivilizationInfos(); iI++)
	{
		m_pbPrereqCivilizations[iI] = false;
	}
	for(int iI = 0; iI < m_iNumPrereqCivilizations; iI++)
		m_pbPrereqCivilizations[GC.getInfoTypeForString(m_aszPrereqCivilizationsforPass3[iI])] = true;
    m_aszPrereqCivilizationsforPass3.clear();

The readpass3 isn't how it is actually written in my code, it is rewritten to work like a "normal array" from here on out. At least it ought to, since I wrote it here on the forums though it quite possibly has a bug or two to sort out.

Notice that I have 3 variables for this one array to load though:
int m_iNumPrereqCivilizations
std::vector<CvString> m_aszPrereqCivilizationsforPass3
bool* m_pbPrereqCivilizations

The last one I control completely during readPass3, and the first two are handled exclusively in the CvXMLLoadUtilitySet function which I wrote.
 
No, that isn't the problem. Your looking at the wrong code. That code is in CvUnitInfo, which works fine without a readpass3, because units load AFTER civics.

Go look at the code in CvBuildInfo. That's what's giving me issues.
 
I was posting examples from my code where I am using this new function in CvXMLLoadUtilitySet, so you can see the proper format. The code I looked at was your code in BuildingInfos, as quoted in the first code segment. The two after that are just examples of what you need to do to fix it.

Note that you must switch to my method of writing the XML as well to use this solution:

Code:
            <PrereqOrCivics>
                <CivicOption>CIVIC_DESPOTISM</CivicOption>
                <CivicOption>CIVIC_SLAVERY</CivicOption>
            </PrereqOrCivics>

Instead of:

Code:
            <PrereqOrCivics>
                <PrereqCivic>
                    <CivicOption>CIVIC_DESPOTISM</CivicOption>
                    <bPrereqCivic>1</bPrereqCivic>
                </PrereqCivic>
                <PrereqCivic>
                    <CivicOption>CIVIC_SLAVERY</CivicOption>
                    <bPrereqCivic>1</bPrereqCivic>
                </PrereqCivic>
            </PrereqOrCivics>
 
Notice that I have 3 variables for this one array to load though:
int m_iNumPrereqCivilizations
std::vector<CvString> m_aszPrereqCivilizationsforPass3
bool* m_pbPrereqCivilizations

The last one I control completely during readPass3, and the first two are handled exclusively in the CvXMLLoadUtilitySet function which I wrote.

Okay, I understand the last boolean array, but where are the first two defined? CvXMLLoadUtilitySet?
 
These types of relationships are already in the game for other objects. I don't think XOR and NOT are needed. :p

Knights require HBR and Guilds and Iron and (Horses or Ivory)*

* Okay, you can't build War Knights, but that would be awesome. I'm pretty sure there's some game object that requires both AND and OR. It's not rocket science, and it's up to the modder to decide if the extra complexity is necessary.

Whether or not you decide you need this, I believe that getting this to work will be a great learning experience for Afforess that can be applied to many more features. :goodjob:
 
  • Building1
    • Requires Civic1 OR Civic2
  • Building2
    • Requires Civic1 AND Civic3
  • Building3
    • Requires Civic2 AND (Civic3 OR Civic4)
In xml, you would write the following. I have assigned each term a letter for reference below.
Code:
building1
   prereqor
      civic1 (a)
      civic2 (b)
building2
   prereqand
      civic1 (c)
      civic3 (d)
building3
   prereqand
      civic2 (e)
   prereqor
      civic3 (f)
      civic4 (g)
The problem is this requires a readpass2,3 because civics aren't defined when buildings are read, and the code to do this involves vectors and resized arrays which are not intuitive; 60 messages have gone here with no working solution yet.

My suggestion is to invert the way you are looking at the xml. I grant that this is less readable. But the code would not need any readpass2,3 and it would use only staticly sized arrays. I write the following:
Code:
civic1
   buildingor
      building1 (a)
   buildingand
      building2 (c)
civic2
   buildingor
      building1 (b)
   buildingand
      building3 (e)
civic3
   buildingand
      building2 (d)
   buildingor
      building3 (f)
civic4
   buildingor
      building3 (g)
You have CvCivic:getBuildingAnd instead of CvBuilding:getCivicAnd, but otherwise there is no difference. Yours gives easier xml, mine gives easier C++.
 
Switch to your method, davidlallen, may make writing the XML-reading code easier (though once you learn it for one type of object you can use it for all other types), but it makes using those fields from CvCivicInfo painful.

To determine if you can construct a building you must loop over all civics. This means using them in code and writing them in XML--the two things that will be done more than once by other modders with less experience--rather than having some skilled C++ coders write the XML-parsing code once.

Besides, Afforess has stated up front a strong desire to learn more C++. :)
 
I agree. But often I beat my head against the wall to force one implementation, when another one would be *almost* as good and would take much less effort.
 
I appreciate the encouragement here, and having a last ditch plan is good, but can anyone help me with the code?

First off, I'm not sure what Xienwolf is talking about. He says I have this in my CvBuildingInfo::Read(),

Code:
	pXML->SetVariableListTagPair(&m_pbPrereqOrCivics, "PrereqOrCivics", sizeof(GC.getCivicInfo((CivicTypes)0)), GC.getNumCivicInfos());

but that isn't true. That isn't in my code. That's for units, and in a completely unrelated section of my SDK. In fact, I didn't even write it. Phungus did, for RevDCM.

This is what is in my CvBuildingInfo::Read() for the civics.

Code:
if (gDLL->getXMLIFace()->SetToChildByTagName(pXML->GetXML(),"PrereqOrCivics"))
	{
		if (pXML->SkipToNextVal())
		{
			int iNumSibs = gDLL->getXMLIFace()->GetNumChildren(pXML->GetXML());
			int iTemp = 0;
			if (iNumSibs > 0)
			{
				if (gDLL->getXMLIFace()->SetToChild(pXML->GetXML()))
				{
					for (int i=0;i<iNumSibs;i++)
					{
						if (pXML->GetChildXmlVal(szTextVal))
						{
                            m_aszPrereqOrCivicsforPass3.push_back(szTextVal);
                            pXML->GetNextXmlVal(&iTemp);
                            m_abPrereqOrCivicsforPass3.push_back(iTemp);
							gDLL->getXMLIFace()->SetToParent(pXML->GetXML());
						}

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

					gDLL->getXMLIFace()->SetToParent(pXML->GetXML());
				}
			}
		}

		gDLL->getXMLIFace()->SetToParent(pXML->GetXML());
	}
	
	if (gDLL->getXMLIFace()->SetToChildByTagName(pXML->GetXML(),"PrereqAndCivics"))
	{
		if (pXML->SkipToNextVal())
		{
			int iNumSibs = gDLL->getXMLIFace()->GetNumChildren(pXML->GetXML());
			int iTemp = 0;
			if (iNumSibs > 0)
			{
				if (gDLL->getXMLIFace()->SetToChild(pXML->GetXML()))
				{
					for (int i=0;i<iNumSibs;i++)
					{
						if (pXML->GetChildXmlVal(szTextVal))
						{
                            m_aszPrereqAndCivicsforPass3.push_back(szTextVal);
                            pXML->GetNextXmlVal(&iTemp);
                            m_abPrereqAndCivicsforPass3.push_back(iTemp);
							gDLL->getXMLIFace()->SetToParent(pXML->GetXML());
						}

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

					gDLL->getXMLIFace()->SetToParent(pXML->GetXML());
				}
			}
		}

		gDLL->getXMLIFace()->SetToParent(pXML->GetXML());
	}

And where are those three variables that Xienwolf talked about located, I know where the last one is, but the other two? And what does he mean handled by CvXMLLoadUtilitySet, the added loop of GC.getNumCivicInfos after reading CvBuildingInfos? Because that is the only thing I've changed there.
 
Looks like I did look at the wrong part of your code then. Could have sworn I double checked. Sorry about that. Will look at your ACTUAL code in a moment here.

The three variables I posted are all for CvInfos.h

Oh, and the bit about using CvXMLLoadUtility is so that you don't have to write the whole bit you posted here twice (once for AND and again for OR, as they both use basically the same thing)


EDIT: Ok, having looked at the proper code in your CvInfos, it does look like it ought to work.

I guess you could change:

int iTemp = 0

to

bool bTemp = false;

Though it should still work fine with an integer, it is PROPER with a boolean.


You do have WoC installed, so try adding this to your ::copyNonDefaults:

Code:
	for ( int i = 0; i < pClassInfo->isPrereqOrCivicsVectorSize(); i++ )
	{
		m_abPrereqOrCivicsforPass3.push_back(pClassInfo->isPrereqOrCivicsNamesVectorElement(i));
		m_aszPrereqOrCivicsforPass3.push_back(pClassInfo->isPrereqOrCivicsValuesVectorElement(i));
	}
 
Looks like I did look at the wrong part of your code then. Could have sworn I double checked. Sorry about that. Will look at your ACTUAL code in a moment here.

The three variables I posted are all for CvInfos.h

It's okay, the search utility fails when there are functions for more than one section of code. Searching for PrereqsAndCivics should yield the right results though, there is only one of that function.
Oh, and the bit about using CvXMLLoadUtility is so that you don't have to write the whole bit you posted here twice (once for AND and again for OR, as they both use basically the same thing)

Oh... That makes sense.
 
EDIT: Ok, having looked at the proper code in your CvInfos, it does look like it ought to work.

I guess you could change:

int iTemp = 0

to

bool bTemp = false;

Though it should still work fine with an integer, it is PROPER with a boolean.


You do have WoC installed, so try adding this to your ::copyNonDefaults:

Code:
    for ( int i = 0; i < pClassInfo->isPrereqOrCivicsVectorSize(); i++ )
    {
        m_abPrereqOrCivicsforPass3.push_back(pClassInfo->isPrereqOrCivicsNamesVectorElement(i));
        m_aszPrereqOrCivicsforPass3.push_back(pClassInfo->isPrereqOrCivicsValuesVectorElement(i));
    }

I changed iTemp to bTemp and added the WoC Code.

Still no success. It looks like the game isn't loading the XML for them. Is there something wrong with my CvBuildingInfo::read()?

WoC has screwed me over in unique ways before, but I've always managed to fix them...

Did you look at my extra loop in CvXMLLoadUtilitySet, was that correct as well?
 
I had not looked at your readpass3 loop in CvXML. Sorry about that.

Code:
	LoadGlobalClassInfo(GC.getBuildingInfo(), "CIV4BuildingInfos", "Buildings", "Civ4BuildingInfos/BuildingInfos/BuildingInfo", false, &CvDLLUtilityIFaceBase::createBuildingInfoCacheObject);
[COLOR="Red"]	for (int i=0; i < GC.getNumBuildingClassInfos(); ++i)
	{
		GC.getBuildingClassInfo((BuildingClassTypes)i).readPass3();
	}
	for (int i=0; i < GC.getNumCivicInfos(); ++i)
	{
		GC.getCivicInfo((CivicTypes)i).readPass3();
	}[/COLOR]
	LoadGlobalClassInfo(GC.getSpecialUnitInfo(), "CIV4SpecialUnitInfos", "Units", "Civ4SpecialUnitInfos/SpecialUnitInfos/SpecialUnitInfo", false);
	LoadGlobalClassInfo(GC.getProjectInfo(), "CIV4ProjectInfo", "GameInfo", "Civ4ProjectInfo/ProjectInfos/ProjectInfo", true);
	LoadGlobalClassInfo(GC.getCivicInfo(), "CIV4CivicInfos", "GameInfo", "Civ4CivicInfos/CivicInfos/CivicInfo", false, &CvDLLUtilityIFaceBase::createCivicInfoCacheObject);
	for (int i=0; i < GC.getNumVoteSourceInfos(); ++i)
	{
		GC.getVoteSourceInfo((VoteSourceTypes)i).readPass3();
	}
	LoadGlobalClassInfo(GC.getLeaderHeadInfo(), "CIV4LeaderHeadInfos", "Civilizations", "Civ4LeaderHeadInfos/LeaderHeadInfos/LeaderHeadInfo", false, &CvDLLUtilityIFaceBase::createLeaderHeadInfoCacheObject);

This is what you have, for reference in case I am looking at the wrong thing again. The part in Red is wrong, and I am hoping that you added both of those.

It is meaningless to call a loop over civics (second loop) till civics actually are loaded, and the readpass3 for buildings needs to wait till after that point so that the civics exist, the entire purpose behind the pass3.

Code:
	LoadGlobalClassInfo(GC.getBuildingInfo(), "CIV4BuildingInfos", "Buildings", "Civ4BuildingInfos/BuildingInfos/BuildingInfo", false, &CvDLLUtilityIFaceBase::createBuildingInfoCacheObject);
	LoadGlobalClassInfo(GC.getSpecialUnitInfo(), "CIV4SpecialUnitInfos", "Units", "Civ4SpecialUnitInfos/SpecialUnitInfos/SpecialUnitInfo", false);
	LoadGlobalClassInfo(GC.getProjectInfo(), "CIV4ProjectInfo", "GameInfo", "Civ4ProjectInfo/ProjectInfos/ProjectInfo", true);
	LoadGlobalClassInfo(GC.getCivicInfo(), "CIV4CivicInfos", "GameInfo", "Civ4CivicInfos/CivicInfos/CivicInfo", false, &CvDLLUtilityIFaceBase::createCivicInfoCacheObject);
	for (int i=0; i < GC.getNumVoteSourceInfos(); ++i)
	{
		GC.getVoteSourceInfo((VoteSourceTypes)i).readPass3();
	}
[COLOR="Lime"]	for (int i=0; i < GC.getNumBuildingClassInfos(); ++i)
	{
		GC.getBuildingClassInfo((BuildingClassTypes)i).readPass3();
	}
[/COLOR]	LoadGlobalClassInfo(GC.getLeaderHeadInfo(), "CIV4LeaderHeadInfos", "Civilizations", "Civ4LeaderHeadInfos/LeaderHeadInfos/LeaderHeadInfo", false, &CvDLLUtilityIFaceBase::createLeaderHeadInfoCacheObject);

So since the second loop was pointless, I dropped it, and now the pass3 happens after the civics, so will be capable of loading real information. Honestly I would prefer that all pass3 loops were at the very end of the function, instead of as soon as the data they desire is available, just to avoid having to ever move them in the future, but that's just me.
 
I had not looked at your readpass3 loop in CvXML. Sorry about that.

Code:
    LoadGlobalClassInfo(GC.getBuildingInfo(), "CIV4BuildingInfos", "Buildings", "Civ4BuildingInfos/BuildingInfos/BuildingInfo", false, &CvDLLUtilityIFaceBase::createBuildingInfoCacheObject);
[COLOR=Red]    for (int i=0; i < GC.getNumBuildingClassInfos(); ++i)
    {
        GC.getBuildingClassInfo((BuildingClassTypes)i).readPass3();
    }
    for (int i=0; i < GC.getNumCivicInfos(); ++i)
    {
        GC.getCivicInfo((CivicTypes)i).readPass3();
    }[/COLOR]
    LoadGlobalClassInfo(GC.getSpecialUnitInfo(), "CIV4SpecialUnitInfos", "Units", "Civ4SpecialUnitInfos/SpecialUnitInfos/SpecialUnitInfo", false);
    LoadGlobalClassInfo(GC.getProjectInfo(), "CIV4ProjectInfo", "GameInfo", "Civ4ProjectInfo/ProjectInfos/ProjectInfo", true);
    LoadGlobalClassInfo(GC.getCivicInfo(), "CIV4CivicInfos", "GameInfo", "Civ4CivicInfos/CivicInfos/CivicInfo", false, &CvDLLUtilityIFaceBase::createCivicInfoCacheObject);
    for (int i=0; i < GC.getNumVoteSourceInfos(); ++i)
    {
        GC.getVoteSourceInfo((VoteSourceTypes)i).readPass3();
    }
    LoadGlobalClassInfo(GC.getLeaderHeadInfo(), "CIV4LeaderHeadInfos", "Civilizations", "Civ4LeaderHeadInfos/LeaderHeadInfos/LeaderHeadInfo", false, &CvDLLUtilityIFaceBase::createLeaderHeadInfoCacheObject);
This is what you have, for reference in case I am looking at the wrong thing again. The part in Red is wrong, and I am hoping that you added both of those.

It is meaningless to call a loop over civics (second loop) till civics actually are loaded, and the readpass3 for buildings needs to wait till after that point so that the civics exist, the entire purpose behind the pass3.

I added some of the read, but not all of it. Let me show you:

Code:
[COLOR=Blue]    LoadGlobalClassInfo(GC.getBuildingInfo(), "CIV4BuildingInfos",  "Buildings", "Civ4BuildingInfos/BuildingInfos/BuildingInfo", false,  &CvDLLUtilityIFaceBase::createBuildingInfoCacheObject);
[/COLOR] [COLOR=Red][COLOR=Blue]    for (int i=0; i <  GC.getNumBuildingClassInfos(); ++i)
    {
        GC.getBuildingClassInfo((BuildingClassTypes)i).readPass3();[/COLOR]
[COLOR=Blue]    }[/COLOR]
    for (int i=0; i < GC.getNumCivicInfos(); ++i)
    {
        GC.getCivicInfo((CivicTypes)i).readPass3();
    }[/COLOR]
[COLOR=Blue]    LoadGlobalClassInfo(GC.getSpecialUnitInfo(), "CIV4SpecialUnitInfos",  "Units", "Civ4SpecialUnitInfos/SpecialUnitInfos/SpecialUnitInfo",  false);
    LoadGlobalClassInfo(GC.getProjectInfo(), "CIV4ProjectInfo",  "GameInfo", "Civ4ProjectInfo/ProjectInfos/ProjectInfo", true);
    LoadGlobalClassInfo(GC.getCivicInfo(), "CIV4CivicInfos", "GameInfo",  "Civ4CivicInfos/CivicInfos/CivicInfo", false,  &CvDLLUtilityIFaceBase::createCivicInfoCacheObject);
    for (int i=0; i < GC.getNumVoteSourceInfos(); ++i)
    {
        GC.getVoteSourceInfo((VoteSourceTypes)i).readPass3();
    }
    LoadGlobalClassInfo(GC.getLeaderHeadInfo(), "CIV4LeaderHeadInfos",  "Civilizations", "Civ4LeaderHeadInfos/LeaderHeadInfos/LeaderHeadInfo",  false, &CvDLLUtilityIFaceBase::createLeaderHeadInfoCacheObject);[/COLOR]

Blue code is Standard BTS. Red is added by me.
So since the second loop was pointless, I dropped it, and now the pass3 happens after the civics, so will be capable of loading real information. Honestly I would prefer that all pass3 loops were at the very end of the function, instead of as soon as the data they desire is available, just to avoid having to ever move them in the future, but that's just me.

I'm not sure why the civic loop would be pointless, isn't that we are doing here, or do we need to do a loop for buildings?
 
Damnit, failure to read on my part again. The loop you did NOT add is BuildingCLASS, so it does belong there.

You are loading civic information, but you are doing it inside of the BuildingInfo, so the loop you need is a loop over buildingInfo, and that loop must happen after civics are loaded. So just remove your Civic loop and insert my building Loop (and leave Firaxis's BuildingClass loop in place), and things should work for you.
 
Damnit, failure to read on my part again. The loop you did NOT add is BuildingCLASS, so it does belong there.

You are loading civic information, but you are doing it inside of the BuildingInfo, so the loop you need is a loop over buildingInfo, and that loop must happen after civics are loaded. So just remove your Civic loop and insert my building Loop (and leave Firaxis's BuildingClass loop in place), and things should work for you.

Now, quick question, should I be looping over BuildingClass's or Buildings?
 
Back
Top Bottom