First Attempt at a Structure

LPlate2

Warlord
Joined
Dec 27, 2018
Messages
299
Hi,

I'm trying to create my first structure in the SDK.
The basic intent is to allow Improvement yields to be changed, depending on the Civilization.

I updated the CIV4TerrainSchema.xml and CIV4ImprovementInfo.xml. My edits were based on the <BonusTypeStructs> structure, which is already used in CIV4ImprovementInfo.xml but there was no need for the equivalent of; <bBonusMakesValid>, <bBonusTrade> & <iDiscoverRand>.
This bit worked fine. Mod could still load up and start.

I then proceeded to modify the cpp and h files, specifically; CvInfos.h, CvInfos.cpp, CvXMLLoadUtility.h and CvXMLLoadUtilitySet.cpp.
Again, I was basing my edits around the BonusTypeStructs, so I searched for ImprovementBonus and edited in similar lines based around a Civilization instead of a Bonus.

The build failed with the following at the end of the Output
Spoiler :

1> Linking DLL
1>CvInfos.obj : error LNK2019: unresolved external symbol "public: void __thiscall CvXMLLoadUtility::InitImprovementCivList(class CvImprovementCivInfo * *,int)" (?InitImprovementCivList@CvXMLLoadUtility@@QAEXPAPAVCvImprovementCivInfo@@H@Z) referenced in function "public: virtual bool __thiscall CvImprovementInfo::read(class CvXMLLoadUtility *)" (?read@CvImprovementInfo@@UAE_NPAVCvXMLLoadUtility@@@Z)
1>CvXMLLoadUtilitySet.obj : error LNK2001: unresolved external symbol "public: void __thiscall CvXMLLoadUtility::InitImprovementCivList(class CvImprovementCivInfo * *,int)" (?InitImprovementCivList@CvXMLLoadUtility@@QAEXPAPAVCvImprovementCivInfo@@H@Z)
1>temp_files\Debug\CvGameCoreDLL.dll : fatal error LNK1120: 1 unresolved externals
1>NMAKE : fatal error U1077: '"C:\Program Files (x86)\Microsoft Visual C++ Toolkit 2003\bin\link.exe"' : return code '0x460'
1> Stop.
1>C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\Microsoft.MakeFile.Targets(38,5): error MSB3073: The command "set TARGET=Debug
1>C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\Microsoft.MakeFile.Targets(38,5): error MSB3073: nmake source_list /NOLOGO
1>C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\Microsoft.MakeFile.Targets(38,5): error MSB3073: nmake fastdep /NOLOGO
1>C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\Microsoft.MakeFile.Targets(38,5): error MSB3073: nmake dll /NOLOGO" exited with code 2.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========


Can someone provide a quick translation of the error message and point me in the direction of what i need to fix?


Edit: Issue partially resolved. I had to edit
CvXMLLoadUtilityInit.cpp also. It now compiles successfully but I'm getting an assert error regarding iListLen, when I load the module. I'm assuming this is pointing to an xml error rather than DLL.


thanks
 
Last edited:
The Assert error I'm getting is,
"Assert Failed

File: .\.\CvXMLLoadUtilityInit.cpp
Line: 260
Expression: (0 < iListLen)
Message: list size to allocate is less than 1"

This is triggered in the last line of the following in CvXMLLoadUtilityInit.cpp,
Spoiler :
Code:
void CvXMLLoadUtility::InitImprovementCivList(CvImprovementCivInfo** ppImprovementCiv, int iListLen)
{
   // SPEEDUP
   PROFILE_FUNC();
   int i;   // loop counter
   CvImprovementCivInfo* paImprovementCiv;

   FAssertMsg(*ppImprovementCiv == NULL,"memory leak?");
   FAssertMsg((0 < iListLen),"list size to allocate is less than 1");

As far as I can see I'm calling this function in CvInfos.cpp
Spoiler :
Code:
// LPlate - Civ Effects on Bonus/Imp
   if (gDLL->getXMLIFace()->SetToChildByTagName(pXML->GetXML(),"CivTypeStructs"))
   {
       // call the function that sets the bonus booleans
       pXML->SetImprovementCiv(&m_paImprovementCiv);
       gDLL->getXMLIFace()->SetToParent(pXML->GetXML());
   }
   else
   {
       // initialize the boolean list to the correct size and all the booleans to false
       pXML->InitImprovementCivList(&m_paImprovementCiv, GC.getNumCivilizationInfos());
   }

// End LPlate - Civ Effects on Bonus/Imp

and in CvXMLLoadUtilitySet.cpp
Spoiler :
Code:
void CvXMLLoadUtility::SetImprovementCiv(CvImprovementCivInfo** ppImprovementCiv)
{
   int i=0;               //loop counter
   int iNumSibs;           // the number of siblings the current xml node has
   TCHAR szNodeVal[256];   // temporarily holds the string value of the current xml node
   CvImprovementCivInfo* paImprovementCiv;   // local pointer to the bonus type struct in memory

   // Skip any comments and stop at the next value we might want
   if (SkipToNextVal())
   {
       // initialize the boolean list to the correct size and all the booleans to false
       InitImprovementCivList(ppImprovementCiv, GC.getNumCivilizationInfos());

It's defined in CvInfos.h as
Spoiler :
Code:
    void InitImprovementCivList(CvImprovementCivInfo** ppImprovementCiv, int iListLen);

I thought that where I was calling InitImprovementCivList, that iListLen was being set to the number of Civilizations (GC.getNumCivilizationInfos()).

Can someone see where I've gone wrong with the above?

I also get an XML Error after each Assert error message,
"For loop iterator is greater than array size
Current XML file is xmll\Terrain/CIV4ImprovementInfos.xml"
I take it this is linked to the problem.
 
Last edited:
I've just checked and apparently CvCivilizationInfo::read is called after CvImprovementInfo::read, so GC.getNumCivilizationInfos() always returns 0 when you're calling InitImprovementCivList and that's where the problem lies. Unfortunately I haven't touched this kind of XML stuff before so I don't really know how to fix this; I'd probably try storing these CvImprovementCivInfos in a vector instead of an array but I have no idea if and how that'd actually work.
 
I've just checked and apparently CvCivilizationInfo::read is called after CvImprovementInfo::read, so GC.getNumCivilizationInfos() always returns 0 when you're calling InitImprovementCivList and that's where the problem lies.

Possibly extremely naive but would I solve this by moving associated calls to Readpass2 or Readpass3 ?
 
Possibly extremely naive but would I solve this by moving associated calls to Readpass2 or Readpass3 ?
You'll need to use readPass3 then. As in readPass3() you won't have access to the XML anymore, you need to read the (string) data in read() and save it for later. IIRC, you're working on an FfH-based mod, so you can check out PrereqCivilization in CvImprovementInfo.
 
Hi,

So looking at the use of PreReqCivilization with CvImprovementInfo
In void bool CvImprovementInfo::read(CvXMLLoadUtility* pXML), we have:
Spoiler :
Code:
    pXML->GetChildXmlValByName(szTextVal, "PrereqCivilization");
   m_aszExtraXML2forPass3.push_back(szTextVal);

In bool CvImprovementInfo::readPass3(), we have:
Spoiler :
Code:
bool CvImprovementInfo::readPass3()
{
   if (m_aszExtraXMLforPass3.size() < 1)
   {
       FAssert(false);
       return false;
   }

   m_iPrereqCivilization = GC.getInfoTypeForString(m_aszExtraXML2forPass3[0]);
   m_aszExtraXML2forPass3.clear();
   m_iSpawnUnitType = GC.getInfoTypeForString(m_aszExtraXMLforPass3[0]);
   m_aszExtraXMLforPass3.clear();

---

The code as I had based it on "BonusTypeStructs" from readPass was:
Spoiler :
Code:
    if (gDLL->getXMLIFace()->SetToChildByTagName(pXML->GetXML(),"CivTypeStructs"))
   {
       // call the function that sets the bonus booleans
       pXML->SetImprovementCiv(&m_paImprovementCiv);
       gDLL->getXMLIFace()->SetToParent(pXML->GetXML());
   }
   else
   {
       // initialize the boolean list to the correct size and all the booleans to false
       pXML->InitImprovementCivList(&m_paImprovementCiv, GC.getNumCivilizationInfos());
   }
Changes would need to be made to this to make it work.
---

If I'm understanding PrereqCivilization approach correctly, then what I'll need to do is include a pushback (m_aszExtraXML3forPass3.push_back(szTextVal);?) for my structure as the Civilization Type within the structure won't be available on the earlier passes.
I'll then need to have the structure information looked for in readPass3.
The steps that I'm going to need to take are:
1) add "std::vector<CvString> m_aszExtraXML3forPass3;" into CvInfos.h
2) Tell it to pushback on reading the structure in bool CvImprovementInfo::read(CvXMLLoadUtility* pXML)
3) Tell it to read the Structure in readPass3

I can't see any examples of this being performed with another Structure. Does anyone know how I should proceed to achieve this?
Alternatively, should I simply change my approach and create an "ImpTypeStructs" within the CivilizationInfos in order to achieve my objective? This should avoid the whole structure and reading order difficulty but I'll lose some the copy/paste benefit of the BonusTypeStructs example.
 
This is an example of how the new proposed structure would look within an ImprovementInfo:
Code:
           <SpawnUnitType>NONE</SpawnUnitType>
           <CivTypeStructs>
               <CivTypeStruct>
                   <CivType>CIVILIZATION_ANIMAL_HUSBANDRY</CivType>
                   <YieldChanges>
                       <iYieldChange>0</iYieldChange>
                       <iYieldChange>2</iYieldChange>
                       <iYieldChange>1</iYieldChange>
                   </YieldChanges>
               </CivTypeStruct>
           </CivTypeStructs>
       </ImprovementInfo>
 
Generally, for something like this, I would advise against using BonusTypeStruct as a template. Instead, I'd use something like SpecialistYieldChanges in BuildingInfos. Now your XML load order problem makes the whole thing a bit harder, I doubt you can just change the XML load order without causing other problems.. The general idea would be to store the structs in a vector in the first pass (keeping the civilization as a string), and then parse the civilization string into an ID in readPass3.

I think the best solution is to do the whole thing in CIV4CivilizationInfos.xml by adapting TerrainYieldChanges.
 
Thanks for the advice. I'll try doing it through CIV4CivilizationInfos.xml.

----

I've given up on the structure. I accomplish what I need for now with the following approach, though its not as elegant or flexible as the structure would have been;
Code:
           <bCivImpUsed>1</bCivImpUsed>
           <CivImps>
               <CivImpType>
                   <Type>IMPROVEMENT_PASTURE</Type>
                   <bCivImp>1</bCivImp>
               </CivImpType>
           </CivImps>
           <CivImpYieldChanges>
               <iYieldChange>0</iYieldChange>
               <iYieldChange>1</iYieldChange>
               <iYieldChange>1</iYieldChange>
           </CivImpYieldChanges>

Although I did give up on the Structure, I have used the ReadPass3 elsewhere in my modding. An example of the edits I made to use this functionality in CvInfos.cpp is below.
Spoiler :

Code:
    pXML->GetChildXmlValByName(m_szPyPerTurn, "PyPerTurn");

// LPlate - GP Wonder Construction
   pXML->GetChildXmlValByName(szTextVal, "BuildingWonder");
   m_aszExtraXMLforPass3.push_back(szTextVal);
   pXML->GetChildXmlValByName(szTextVal, "BuildingWonderConstruction");
   m_aszExtraXMLforPass3.push_back(szTextVal);
   pXML->GetChildXmlValByName(szTextVal, "ImpWonder");
   m_aszExtraXMLforPass3.push_back(szTextVal);
   pXML->GetChildXmlValByName(&m_bConstructingWonder, "bConstructingWonder");
// End LPlate - GP Wonder Construction

   pXML->GetChildXmlValByName(szTextVal, "UnitArtStyleType");
   m_aszExtraXMLforPass3.push_back(szTextVal);
Code:
//FfH: Added by Kael 09/09/2007
bool CvPromotionInfo::readPass3()
{
   if (m_aszExtraXMLforPass3.size() < 1)
   {
       FAssert(false);
       return false;
   }
// LPlate - Original unmodified FFH2 code
//   if (m_aszExtraXMLforPass3[0] != "")
//   {
//       m_iUnitArtStyleType = GC.getInfoTypeForString(m_aszExtraXMLforPass3[0]);
//   }
// End LPlate - Original unmodified FFH2 code
// LPlate - GP Wonder Construction
   if (m_aszExtraXMLforPass3[0] != "")
   {
       m_iBuildingWonder = GC.getInfoTypeForString(m_aszExtraXMLforPass3[0]);
   }
   if (m_aszExtraXMLforPass3[1] != "")
   {
       m_iBuildingWonderConstruction = GC.getInfoTypeForString(m_aszExtraXMLforPass3[1]);
   }
   if (m_aszExtraXMLforPass3[2] != "")
   {
       m_iImpWonder = GC.getInfoTypeForString(m_aszExtraXMLforPass3[2]);
   }
   if (m_aszExtraXMLforPass3[3] != "")
   {
       m_iUnitArtStyleType = GC.getInfoTypeForString(m_aszExtraXMLforPass3[3]);
   }
// End LPlate - GP Wonder Construction
    m_aszExtraXMLforPass3.clear();
   return true;
}
//FfH: End Add
 
Last edited:
Top Bottom