• Our friends from AlphaCentauri2.info are in need of technical assistance. If you have experience with the LAMP stack and some hours to spare, please help them out and post here.

Quick Modding Questions Thread

As part of the work I'm doing for the save game editor, I've looked into how Civilization 4 loads XML files. For those who are interested, details follow:

Civilization 4 XML files are read using the algorithm described below. Some details, such as the use of schema for XML validation are omitted. This
description is Windows-specific; path names will change somewhat for Macintosh. The description focuses on XML files that have an impact on the binary
layout of Civilization 4 save game files; XML files not affected the save game layout might be treated somewhat differently (I have not tested such files,
hence the caveat). Finally, a modded Civ4 DLL can of course make changes to the XML loading procedure described below.

1. XML loading begins by recursively scanning the following directories, in the order listed, for XML files:

* Mod: %CIV4_INSTALL_ROOT%/Mods/%MOD_NAME%/Assets/XML
* CustomAssets: %MY_GAMES%/Beyond the Sword/CustomAssets/XML
* BTS: %CIV4_INSTALL_ROOT%/Assets/XML
* Warlords: %CIV4_INSTALL_ROOT%/../Warlords/Assets/XML
* Vanilla: %CIV4_INSTALL_ROOT%/../Assets/XML

In the list above, I've enclosed values with % to simplify names. Substitute the actual path to the game's installation root directory for
%CIV4_INSTALL_ROOT%, the name of the mod being loaded for %MOD_NAME%, and the path to your "My Games" directory for %MY_GAMES%. If a Mod is not being loaded,
the corresponding reference to "Mod:" is skipped.

2. Once an XML file is found in Step 1 above, an internal Civilization 4 component which the SDK refers to as the "File Manager" stores the file name and its full path. If that same
file name is found later in the search, it is ignored. That is, the first occurrence of a name takes precedence, masking later occurrences of the same name. For example, if
CustomAssets/XML/GameInfo/CIV4SeaLevelInfo.xml is found, CIV4SeaLevelInfo.xml would be stored and its path would be set to CustomAssets/XML/GameInfo. Later in the search, if
CIV4SeaLevelInfo.xml were found, say at %CIV4_INSTALL_ROOT%/../Warlords/Assets/XML/GameInfo/CIV4SeaLevelInfo.xml, the later occurrence would be ignored.

3. Civilization 4 then loads XML files as controlled by the Civilization 4 DLL. Only those files specifically mentioned in the DLL are loaded. Stray XML files not referenced in
the DLL are unused.

4. When referring to an XML file, the DLL uses a path name relative to the parent XML directory such as "GameInfo". For example, to load CIV4SeaLevelInfo.xml, the DLL would
request that the File Manager load CIV4SeaLevelInfo.xml using the relative path GameInfo. The File Manager would then attempt to load the requested file using the file names
it stored in Step 1. Both the file name and relative path need to match for loading to succeed.

5. Two main functions are used in the DLL to load XML files with impact on save game layout: CvXMLLoadUtility::ReadGlobalDefines and CvXMLLoadUtility::LoadGlobalClassInfo.
ReadGlobalDefines reads simple variables (e.g., ints, floats, etc.) from GlobalDefinesAlt.xml and then from GlobalDefines.xml with values in GlobalDefinesAlt.xml taking precedence.
LoadGlobalClassInfo is used to read complex variables, corresponding to C++ classes/structs/enums. Each such class instance is stored in a vector. For example, the content of
Civ4SeaLevelInfo.xml, after removing comments, is:
Code:
    <Civ4SeaLevelInfo xmlns="x-schema:CIV4GameInfoSchema.xml">
    <SeaLevelInfos>
        <SeaLevelInfo>
            <Type>SEALEVEL_LOW</Type>
            <Description>TXT_KEY_SEALEVEL_LOW</Description>
            <iSeaLevelChange>-8</iSeaLevelChange>
        </SeaLevelInfo>
        <SeaLevelInfo>
            <Type>SEALEVEL_MEDIUM</Type>
            <Description>TXT_KEY_SEALEVEL_MEDIUM</Description>
            <iSeaLevelChange>0</iSeaLevelChange>
        </SeaLevelInfo>
        <SeaLevelInfo>
            <Type>SEALEVEL_HIGH</Type>
            <Description>TXT_KEY_SEALEVEL_HIGH</Description>
            <iSeaLevelChange>6</iSeaLevelChange>
        </SeaLevelInfo>
      </SeaLevelInfos>
    </Civ4SeaLevelInfo>

LoadGlobalClassInfo reads this file, and creates three instances of CvSeaLevelInfo, one for each <SeaLevelInfo> node in the XML.

6. Some Mods augment the XML loading process described above using something called "ModularLoading." If ModularLoading is enabled for a mod, Step 4 is modified to add the following additional
sub-steps:

4a. XML loading proceeds as normal, using the file name and relative path as previously described.
4b. Once 4a is done, another search for XML files occurs. This time, the original file name is prefixed with "*_" where * matches any legal file name character. For example, if the original
request was for CIV4SeaLevelInfo.xml, the wild card file name would be *_CIV4SeaLevelInfo.xml, and the wildcard name would match file names such as Minoa_CIV4SeaLevelInfo.xml.
When importing simple types, modular loading is applied to GlobalDefines.xml, but not to GlobalDefinesAlt.xml.
4c. The wildcard name generated in Step 4b is used to search for files in the Mod's "Modules" directory. For example, if the Mod name is "Minoans", the Modules directory would be
%CIV4_INSTALL_ROOT%/Mods/Minoans/Modules. When performing the file search in Step 4c, the search is recursive. All directories and their children are searched using the wildcard file name.
4d. For each matching XML file found in 4c, the corresponding XML is read. Values read add to any existing definitions, overwriting existing values only when <DefineName> (in the case of
simple types) or <Type> (in the case of complex types) already exist. For example, if CIV4GameInfoSchema.xml were read as described in Step 5 and then Minoan_CIV4GameInfoSchema.xml
were found, having the following content:

Code:
        <Civ4SeaLevelInfo xmlns="x-schema:Minoan_CIV4GameInfoSchema.xml">
        <SeaLevelInfos>
            <SeaLevelInfo>
                <Type>SEALEVEL_MEDIUM</Type>
                <Description>TXT_KEY_SEALEVEL_MEDIUM</Description>
                <iSeaLevelChange>1</iSeaLevelChange>
            </SeaLevelInfo>
            <SeaLevelInfo>
                <Type>SEALEVEL_MEDIUM_HIGH</Type>
                <Description>TXT_KEY_SEALEVEL_HIGH</Description>
                <iSeaLevelChange>2</iSeaLevelChange>
            </SeaLevelInfo>
          </SeaLevelInfos>
        </Civ4SeaLevelInfo>

the existing CvSeaLevelInfo with type SEALEVEL_MEDIUM would be replaced by the new node with the same type; a new CvSeaLevelInfo instance would be created for the new node
corresponding to SEALEVEL_MEDIUM_HIGH, and the existing CvSeaLevelInfo instance for SEALEVEL_HIGH would be unchanged.
 
Last edited:
I would love to add some (other) languages in my copy of Civ4, I spend so much time in Civ4 anyway, I may aswell work on my secondary languages and widen the scope of my MOD for all


Some of the creation on this website give in the Text XML file.
One of many example (the one I ran into today) - The Savanna fedature (https://forums.civfanatics.com/resources/savanna-with-ambient-sound-effect-new.30383/),
It has 13 languages instead of the initial 5 (it's not just a language swap)

<TEXT>
<Tag>TXT_KEY_FEATURE_SAVANNA</Tag>
<English>Savanna</English>
<French>Savanna</French>
<German>Savanne</German>
<Italian>Savana</Italian>
<Spanish>Sabana</Spanish>
<Portuguese>Savana</Portuguese>
<Polish>Sawanna</Polish>
<Hungarian>Szavanna</Hungarian>
<Russian>Саванна</Russian>
<Turkish>Savan</Turkish>
<ChineseSimp>疏林草原</ChineseSimp>
<Japanese>サバナ</Japanese>
<Korean>사바나</Korean>
</TEXT>
</Civ4GameText>


Where can I find a version and/or a way to amend my version to have more languages ? I've looked for this many times with no success whatsoever
 
Last edited:
@dbkblk has added several extra languages to AND2. CoM is a succession nod of AND2 and the above xml lines are from CoM. However AND2 never had a Savannah feature, I added it to CoM but I don't remember exactly if I took it from PAEor somewhere else.
 
Where can I find a version and/or a way to amend my version to have more languages ? I've looked for this many times with no success whatsoever
If it's just adding tags for languages, you can use cxt to clone English, but it's interface is in russian, so may be troublesome, unless you only 1 text XML to cover everything. It was originally wrote for WinXP and it sometimes bugs out if you use it for actual editing, so you are probably better off using proper XML editors. This one looks ill-suited for translation. Also at least russian requires encoding, other non-latin languages likely too. There are a few translators still active on C2C's Discord, they would probably provide better advice.
 
Thanks team, I will ask @dbkblk and see how has done for a start...
Will let you know (perhaps in next version of BTG!)
Let me know if you can't reach him.
 
Is there a way for me to let great generals give other promotions to a unit? (Other than leadership and +20XP)
 
Is there a way for me to let great generals give other promotions to a unit? (Other than leadership and +20XP)
<bLeader>1 marks what's given in CIV4PromotionInfos.xml. If you want something more complicated, you'd probably need to make a new mission and that requires recompiling DLL.
 
<bLeader>1 marks what's given in CIV4PromotionInfos.xml. If you want something more complicated, you'd probably need to make a new mission and that requires recompiling DLL.
Ahhhh... I see. I never understood either how that works until now.
However, I know that C2C and/or Col has different promotions available with warlord like units. I'd start asking those modders for help directly.
 
Ok thanks. Also, is there a way to make disallow making the same unit twice (sort of like a national unit) but even if it dies you can't make another one?
 
Ok thanks. Also, is there a way to make disallow making the same unit twice (sort of like a national unit) but even if it dies you can't make another one?
You could check out World Units in Fall from Heaven 2. Most likely it's coded in DLL, again.
 
I would like to make a mod, but before I must know if certain things are possible. If not, then at least I would have to find a workaround from the start or redesign my idea and not lose time discovering with trial and error that it can't be done.
I ask here whether or not something is possible, a simple Yes/No is enough.
The most crucial thing for me, at this stage, is to know what can and can't be done.
Of course, if you want to give me tips, facilitate some links/tutorials, throw me some extra info, or just magnanimously tell me how to do it, I would be extremely thankful.
In any case, thanks in advance.

1.- A civic that can be locked or unlocked by another civic.
For example, selecting Universal Suffrage (Government Civic) disables Serfdom (Labour Civic).
Is it possible?

2.- An Improvement that depends on adjacencies.
For example, the Farm must be built next to a Village.
Is it possible?

3.- An improvent that must be built on top of another improvement (in the same tile) and would replace the original improvement. Think about it like a level 2 of the same improvement.
I'm not asking if 2 improvements can share the same tile.
For example, the Farm Level 2 requires a Farm Level 1 on the tile to be available for construction. When the worker builds it, the Farm Level 1 is deleted and replaced by the Farm Level 2.
Is it possible?

4.- Divorcing Culture levels from territorial expansion.
For example, a City evolves from Poor to Fledging, but its borders do not grow.
Is it possible?

5.- Add more thresholds to the city (including UI), like Growth, Production and Culture.
For example, a threshold for Gold income. When the bar fills, you get +1 free Merchant, it resets and increases threshold size.
Is it possible?
 
@Baruna
Quick answers: Yes to all if you are willing and able to edit the dll :)

1. Yes. My mod has a civic dependency system just what you have described. Some civics block others.
@keldath also has one, AFAIK his system unlocks other civics.

2. @devolution's Port District does just that.

3. I'm certain it is possible though I'm not a programmer. An improvement can have a terrain, a feature, a resource, etc as a requirement and I see no reason why the code couldn't be tweaked in this way. One improvement replacing an other (upgrading) is already possible.

4. Yes, many mods do that, including mine. There's a modcomp for that but I don't remember the name. Perhaps someone can help me out :crazyeye:

5. Like @platyping's Faith modcomp? :)
 
Hi ,
As my good friend @<Nexus> stated, all if the above are possible.

Personally i applied the civics, both of the improvements ideas and the last one of culture.

In my mod, per the civics,
My implementation is exactly the same as you wrote. I created government civics with its own sub civics.
There is plenty of code , mostly on how ai handles it.
As far as i know, no one implemented the system that ive made.
The dependentcy civics exists, but not as elaborated as i wrote.

Cheers
 
A barrage of good news!
By your mod, you mean Chronicles of Mankind?

I will play and study all referenced mods, thank you again.
May see me again with less theoretical questions.

Edit:
@keldath
"mostly on how ai handles it."
Yep, this will probably be the theme of my next batch of questions. But all in due time.
edit 2:
@keldath
Also what mod do you mean, I see5 mods in your signature
 
Last edited:
By your mod, you mean Chronicles of Mankind?
Yes, it is a modmod of Rise of Mankind- A New Dawn 2.

edit 2:
@keldath
Also what mod do you mean, I see5 mods in your signature
The one he's referring to is Dawn of the Overlords, that is a modmod of AdvCiv.
 
I have a problem in my mod:

Fortify the Ruins?

Whenever you conquer a city, you get the option to "Raze the city and Fortify the ruins" for quite a lot of money.
I would asume that you destroy the city and builds a Fort immediately, but that is not the case. Nothing is build. Only the city ruins remain.
I believe these are the code responsible:

C++:
int CvPlayer::getRazeAndFortifyCost(CvCity* pCity) const
{
    int iFort = GC.getInfoTypeForString("IMPROVEMENT_FORT");
    if (iFort == -1)
    {
        return -1;
    }
    bool bHasTech = false;
    for (int iI = 0; iI < GC.getNumBuildInfos(); iI++)
    {
        if (GC.getBuildInfo((BuildTypes)iI).getImprovement() == iFort)
        {
            TechTypes ePrereqTech = (TechTypes)GC.getBuildInfo((BuildTypes)iI).getTechPrereq();
            if (ePrereqTech == NO_TECH || GET_TEAM(getTeam()).isHasTech(ePrereqTech))
            {
                bHasTech = true;
                break;
            }
        }
    }
    if (!bHasTech)
    {
        return -1;
    }
    int iBaseCost = GC.getDefineINT("BASE_RAZE_AND_FORTIFY_CAPTURED_CITY_COST", 100);
    iBaseCost *= (pCity->getPopulation() + 17);
    iBaseCost /= 18;
    iBaseCost *= (getCurrentEra() / 2);
    iBaseCost *= (1 + getNumCities());
    iBaseCost /= (1 + GC.getWorldInfo(GC.getMapINLINE().getWorldSize()).getTargetNumCities());

    return std::max(iBaseCost, GC.getDefineINT("BASE_RAZE_AND_FORTIFY_CAPTURED_CITY_COST", 100));

}
C++:
bool CvDLLButtonPopup::launchRazeCityPopup(CvPopup* pPopup, CvPopupInfo &info)
{
    CvPlayer& player = GET_PLAYER(GC.getGameINLINE().getActivePlayer());

    CvCity* pNewCity = player.getCity(info.getData1());
    if (NULL == pNewCity)
    {
        FAssert(false);
        return (false);
    }

    if (0 != GC.getDefineINT("PLAYER_ALWAYS_RAZES_CITIES"))
    {
        player.raze(pNewCity);
        return false;
    }

    PlayerTypes eHighestCulturePlayer = (PlayerTypes)info.getData2();

    int iCaptureGold = info.getData3();
    bool bRaze = player.canRaze(pNewCity);
    bool bGift = ((eHighestCulturePlayer != NO_PLAYER)
        && (eHighestCulturePlayer != player.getID())
        && ((player.getTeam() == GET_PLAYER(eHighestCulturePlayer).getTeam()) || GET_TEAM(player.getTeam()).isOpenBorders(GET_PLAYER(eHighestCulturePlayer).getTeam()) || GET_TEAM(GET_PLAYER(eHighestCulturePlayer).getTeam()).isVassal(player.getTeam())));

    bRaze = player.canRaze(pNewCity);

    CvWString szBuffer;
    if (iCaptureGold > 0)
    {
        szBuffer = gDLL->getText("TXT_KEY_POPUP_GOLD_CITY_CAPTURE", iCaptureGold, pNewCity->getNameKey());
    }
    else
    {
        szBuffer = gDLL->getText("TXT_KEY_POPUP_CITY_CAPTURE_KEEP", pNewCity->getNameKey());
    }
    gDLL->getInterfaceIFace()->popupSetBodyString(pPopup, szBuffer);
    gDLL->getInterfaceIFace()->popupAddGenericButton(pPopup, gDLL->getText("TXT_KEY_POPUP_KEEP_CAPTURED_CITY").c_str(), NULL, 0, WIDGET_GENERAL);

    if (bRaze)
    {
        gDLL->getInterfaceIFace()->popupAddGenericButton(pPopup, gDLL->getText("TXT_KEY_POPUP_RAZE_CAPTURED_CITY").c_str(), NULL, 1, WIDGET_GENERAL);

        int iRazeAndFortifyCost = player.getRazeAndFortifyCost(pNewCity);
        if (iRazeAndFortifyCost > 0 && player.getGold() > iRazeAndFortifyCost)
        {
            gDLL->getInterfaceIFace()->popupAddGenericButton(pPopup, gDLL->getText("TXT_KEY_POPUP_RAZE_AND_FORTIFY_CAPTURED_CITY", iRazeAndFortifyCost).c_str(), NULL, 4, WIDGET_GENERAL);
        }
    }
    if (bGift)
    {
        szBuffer = gDLL->getText("TXT_KEY_POPUP_RETURN_ALLIED_CITY", GET_PLAYER(eHighestCulturePlayer).getCivilizationDescriptionKey());
        gDLL->getInterfaceIFace()->popupAddGenericButton(pPopup, szBuffer, NULL, 2, WIDGET_GENERAL, 2, eHighestCulturePlayer);
    }
    gDLL->getInterfaceIFace()->popupAddGenericButton(pPopup, gDLL->getText("TXT_KEY_CITY_WARNING_ANSWER3").c_str(), NULL, 3, WIDGET_GENERAL, -1, -1);
    gDLL->getInterfaceIFace()->popupLaunch(pPopup, false, POPUPSTATE_IMMEDIATE);
    gDLL->getInterfaceIFace()->playGeneralSound("AS2D_CITYCAPTURE");

    return (true);
}

Anyone sees any problems with it why no Fort is placed when the option chosen?
 
Back
Top Bottom