xml load order?

davidlallen

Deity
Joined
Apr 28, 2008
Messages
4,743
Location
California
I am trying to add a feature where certain civilizations are forbidden from founding or converting to certain religions. There is a python hook to prevent founding, via doHolyCity / doHolycityTech, but there is no python hook to prevent converting.

I have hard-coded a new function CvReligionInfo::isForbidden(eCivilizationType) and put calls to this function all the places it needs to go, and it is working. However, it has to be driven by xml. So I have added schema and sdk code for this, in gameinfo/civ4religioninfo.xml:
Code:
<ForbiddenCivs>
   <ForbiddenCiv>
      <CivName>CIVILIZATION_FOO</CivName>
      <bForbidden>1</bForbidden>
   </ForbiddenCiv>
   <ForbiddenCiv>
      <CivName>CIVILIZATION_BAR</CivName>
      <bForbidden>1</bForbidden>
   </ForbiddenCiv>
</ForbiddenCivs>

However, this does not work, because when reading the xml, it calls GC.getNumCivilizationInfos(), which returns 0. Then the code tries to allocate 0 bytes and throws an xml parsing error.

This implies that I cannot put civilization infos onto religions, because the xml from assets/gameinfo is read before the xml from assets/civilizations. If that is true, then I can rewrite the code to be like CvCivilizationInfo::isForbidden(eReligionType) and put the xml into the civilization xml, listing the religions inside.

Is that correct? If so, is there a canonical list of the order in which directories are read, so that I can avoid this mistake in the future?
 
Now, keep in mind, this is a stab in the dark, but did you try CvXMLLoadUtilitySet.cpp?

Try adding a readPass3() to CvCivilizationInfos in CvInfos.cpp, then add:

Spoiler :
Code:
	LoadGlobalClassInfo(GC.getUnitArtStyleTypeInfo(), "CIV4UnitArtStyleTypeInfos", "Civilizations", "Civ4UnitArtStyleTypeInfos/UnitArtStyleTypeInfos/UnitArtStyleTypeInfo", false);
	LoadGlobalClassInfo(GC.getCivilizationInfo(), "CIV4CivilizationInfos", "Civilizations", "Civ4CivilizationInfos/CivilizationInfos/CivilizationInfo", true, &CvDLLUtilityIFaceBase::createCivilizationInfoCacheObject);
	for (int i=0; i < GC.getNumReligionInfos(); ++i)
	{
		GC.getReligionInfo((ReligionTypes)i).readPass3();
	}
	LoadGlobalClassInfo(GC.getHints(), "CIV4Hints", "GameInfo", "Civ4Hints/HintInfos/HintInfo", false);
	LoadGlobalClassInfo(GC.getMainMenus(), "CIV4MainMenus", "Art", "Civ4MainMenus/MainMenus/MainMenu", false);

Not sure if it'll work, but it never hurts to try.
 
What TheLadiesOgre is getting at is that when reading the religions you must store the civilization XML key. After the civilizations have been loaded you call a new function (readPass3() in WoC I believe) that allocates the CivilizationTypes array and looks up the civ keys using getInfoTypeForString().

CvXMLLoadUtilitySet.cpp is what controls the loading order of the XML files, and I don't know if pulling civilizations up before religions will cause some other ordering issues. That would be easier, so it couldn't hurt to try. Or maybe push religions after it (but before leaders).
 
Thanks for the tips. I have read the readPass3 functions, and I don't think the existing ones quite solve my problem. For the unitclassinfo and buildingclassinfo pass3 functions, it is just a matter of setting a single int field. For what I want to do, I need to allocate a whole array. The size of the array is not known when the religion is loaded, because religion is loaded before civilization. It may be possible to allocate the space during readPass3, but there is no existing code to use as a model.

By looking in CvXMLLoadUtilitySet.cpp, I can see that the order of loading xml's is more complicated than I thought; it is based on individual files and not directories. I can trace it out; both religioninfo and civilizationinfo are loaded in the premenu step, but religion is very early and civilization is very late. I am pretty afraid that just jumping up civilization all that way in the list will introduce some other dependency problem.

But now I can be confident of the order of loading, so I am sure that moving the data to the civilization object will work.
 
Granted you've already done all but the loading work for this feature, but the other way to do it is to store the banned religions for each civilization in the civilization itself. This gets around the loading order and in my opinion is the more logical place to store the data.

  • When a modder adds a new civilization (likely), they can put the religions it can't adopt in it rather than having to modify the religions.
  • Load order is taken care of because the religions are already loaded when the civilization needs them.
However, what I was suggesting above was to use a list (resizable array such as STL vector) to store the civilization XML keys in the religion during the first read pass. You don't need to know how many max civilizations there are because the list will grow to the number of civilizations banned from adopting that religion automatically.

In readPass3(), after the civilizations have been loaded, you allocate a bool array based on the number of civilizations and look up each XML key using getInfoTypeForString(). Use the returned value to index into the array and set the element to true.
 
Granted you've already done all but the loading work for this feature, but the other way to do it is to store the banned religions for each civilization in the civilization itself.

We could debate whether it is more logical to store a list of civs on the religion, or a list of religions on the civ. However, it seems that the latter is an easy implementation without modifying the xml load order or introducing STL vectors.
 
Top Bottom