Note: DLL modding requires a working knowledge of C++.
Follow the steps here to download, install, and get a working project of the DLL up.
This tutorial is an expansion of the XML to Lua tutorial by Pazyryk.
This tutorial is going to create a new Column to the Units table called "Mana".
Create a new column using SQL, like follows:
Now we've added the Mana tag to the Units table, meaning we can apply it anywhere. For example:
Nothing new yet, right? Well, now, it's time to open up the source code and get your tag recognized by the game core.
In order to do this step you must identify which header file contains the data stored for your source. A quick list from my experiences in the DLL:
Obviously you may have to do some digging to find where you need to go!
Either way, let's open up CvUnitClasses.h because that's where the game handles loading our database into the game.
Inside CvUnitClasses.h, navigate to the CvUnitEntry class. You will see a great amount of accessor functions (GetRange(), for example) and defined variables (m_iRange, for example). Let's create a new variable, so it looks like follows:
Please note the variable is named "m_iMana" because m_ stands for "member" and i stands for "integer", which helps you quickly identify the variable's purpose just by its name.
note: the variable type will depend on what column you're loading in. If it's an integer, you'll declare an int, if it's a boolean, you'll declare a bool.
Also create a new member accessor function.
note: Your function return type must match the integer type. If it's an integer, you'll return an int, if it's a boolean, you'll return a bool.
Warning: Please remember to create a comment differentiating your code from the Firaxis code! This stay may save hours of confusion in the future!
After that, we are done in the header and can now work on the UnitClasses.cpp file. Locate the CvUnitEntry section of the file (Visual Studio allows for quick navigation via the dropdowns below the file names). Navigate to the constructor, which shares the name of your class, so it looks like follows:
Next we must load our variable a value from the XML tables. Head to the CacheResults() function and create a new entry into the function, keeping in mind that you must match the name inside the parentheses with the name of your column in the XML.
Finally, we must define our function that we created in the header file, GetMana(). The code to this is follows, and the format will be the same no matter what variable you choose:
We are now done with UnitClasses.h. Now, for most other things, you would be done around here. Now you would be able to access your column's data from anywhere in the source code, for you to use at will. But, Units are special, and it's why I chose to use them. UnitClasses.h handles all the unit properties: movement, range, etc. but it doesn't handle their behavior. This is handled in the CvUnit object. Thus, open up CvUnit.h, we have to make some additions.
I assume you understand the basics of navigating from function to function from now on, so I won't be using anymore images.
In the CvUnit.h file, navigate to the CvUnit class declaration, and create a new member function
The function will link back into the object's class association and pull their data.
That's it, now we enter the CvUnit.cpp file and define our function:
Whoa, whoa? What's going on here? What's this VALIDATE_OBJECT and m_pUnitInfo nonsense? Well, VALIDATE_OBJECT, for the purposes of your needs, will be useless. It's tied to asserts, which is useful for debugging, but not something you need to concern yourself with. That being said, for safety include it. m_pUnitInfo, however, is much more important. It's a pointer to your unit info, or, the information defined in the CvUnitEntry class (what we modified in CvUnitClasses). Since UnitEntry stores all of the data about our unit (from the XML tables), we use it to help us out. This function thus just returns the value of our mana. Once we have this function, anywhere in our CvUnit.cpp we can call GetMana() and it will give us the correct value.
That's it for loading a column into the C++ database! Probably a lot more complicated than the Lua, but much more robust! The possibilities with the C++ are endless!
For testing purposes, I will create a hook into Lua to call our new function. Open up CvLuaUnit.h and CvLuaUnit.cpp.
In the CvLuaUnit.h header file, create a new function:
And in the CvLuaUnit.cpp file, you must define that new function:
If you were returning a boolean function, the main lua function lGetMana would still return an integer, but the functions inside of it would change (int iResult would become bool bResult and lua_pushinteger would become lua_pushboolean).
And "push" it into Lua, via the PushMethods() function in CvLuaUnit.cpp
Aaannnndd we're done! Hopefully that wasn't so hard.
=== Testing In-Game ===
Because we hooked our function into Lua, we can test it in game.
Load your mod and remember to set VFS to True for your DLL. Open up the FireTuner and load the game up. Create an archer, and select your Warrior. Enter the following commands into the tuner:
The game should print out "100".
Select your archer.
The game should print out "47".
Congratulations! You've now loaded your new column into the game via the DLL! Now mess around! The possibilities are endless!
Follow the steps here to download, install, and get a working project of the DLL up.
This tutorial is an expansion of the XML to Lua tutorial by Pazyryk.
This tutorial is going to create a new Column to the Units table called "Mana".
Create a new column using SQL, like follows:
Code:
ALTER TABLE Units ADD COLUMN 'Mana' integer default 0;
Now we've added the Mana tag to the Units table, meaning we can apply it anywhere. For example:
Code:
<Units>
<Update>
<Where Type="UNIT_WARRIOR"/>
<Set Mana="100"/>
</Update>
<Update>
<Where Type="UNIT_ARCHER"/>
<Set Mana="47"/>
</Update>
</Units>
Nothing new yet, right? Well, now, it's time to open up the source code and get your tag recognized by the game core.
In order to do this step you must identify which header file contains the data stored for your source. A quick list from my experiences in the DLL:
Code:
Units - CvUnitClasses.h
Buildings - CvBuildingClasses.h
Policies - CvPolicyClasses.h
Beliefs - CvBeliefClasses.h
Religion - CvReligionClasses.h
Resources - CvInfos.h
Eras - CvInfos.h
Obviously you may have to do some digging to find where you need to go!
Either way, let's open up CvUnitClasses.h because that's where the game handles loading our database into the game.
Inside CvUnitClasses.h, navigate to the CvUnitEntry class. You will see a great amount of accessor functions (GetRange(), for example) and defined variables (m_iRange, for example). Let's create a new variable, so it looks like follows:
Spoiler :

Please note the variable is named "m_iMana" because m_ stands for "member" and i stands for "integer", which helps you quickly identify the variable's purpose just by its name.
note: the variable type will depend on what column you're loading in. If it's an integer, you'll declare an int, if it's a boolean, you'll declare a bool.
Also create a new member accessor function.
Spoiler :

note: Your function return type must match the integer type. If it's an integer, you'll return an int, if it's a boolean, you'll return a bool.
Warning: Please remember to create a comment differentiating your code from the Firaxis code! This stay may save hours of confusion in the future!
After that, we are done in the header and can now work on the UnitClasses.cpp file. Locate the CvUnitEntry section of the file (Visual Studio allows for quick navigation via the dropdowns below the file names). Navigate to the constructor, which shares the name of your class, so it looks like follows:
Spoiler :

Next we must load our variable a value from the XML tables. Head to the CacheResults() function and create a new entry into the function, keeping in mind that you must match the name inside the parentheses with the name of your column in the XML.
Spoiler :

Finally, we must define our function that we created in the header file, GetMana(). The code to this is follows, and the format will be the same no matter what variable you choose:
Code:
/// Unit's Mana
int CvUnitEntry::GetMana() const
{
return m_iMana;
}
We are now done with UnitClasses.h. Now, for most other things, you would be done around here. Now you would be able to access your column's data from anywhere in the source code, for you to use at will. But, Units are special, and it's why I chose to use them. UnitClasses.h handles all the unit properties: movement, range, etc. but it doesn't handle their behavior. This is handled in the CvUnit object. Thus, open up CvUnit.h, we have to make some additions.
I assume you understand the basics of navigating from function to function from now on, so I won't be using anymore images.
In the CvUnit.h file, navigate to the CvUnit class declaration, and create a new member function
Code:
int GetMana() const;
That's it, now we enter the CvUnit.cpp file and define our function:
Code:
// --------------------------------------------------------------------------------
int CvUnit::GetMana() const
{
VALIDATE_OBJECT
return (m_pUnitInfo->GetMana());
}
Whoa, whoa? What's going on here? What's this VALIDATE_OBJECT and m_pUnitInfo nonsense? Well, VALIDATE_OBJECT, for the purposes of your needs, will be useless. It's tied to asserts, which is useful for debugging, but not something you need to concern yourself with. That being said, for safety include it. m_pUnitInfo, however, is much more important. It's a pointer to your unit info, or, the information defined in the CvUnitEntry class (what we modified in CvUnitClasses). Since UnitEntry stores all of the data about our unit (from the XML tables), we use it to help us out. This function thus just returns the value of our mana. Once we have this function, anywhere in our CvUnit.cpp we can call GetMana() and it will give us the correct value.
That's it for loading a column into the C++ database! Probably a lot more complicated than the Lua, but much more robust! The possibilities with the C++ are endless!
For testing purposes, I will create a hook into Lua to call our new function. Open up CvLuaUnit.h and CvLuaUnit.cpp.
In the CvLuaUnit.h header file, create a new function:
Code:
static int lGetMana(lua_State* L);
Code:
//------------------------------------------------------------------------------
//int GetMana();
int CvLuaUnit::lGetMana(lua_State* L)
{
CvUnit* pkUnit = GetInstance(L);
const int iResult = pkUnit->GetMana();
lua_pushinteger(L, iResult);
return 1;
}
And "push" it into Lua, via the PushMethods() function in CvLuaUnit.cpp
Code:
Method(GetMana); // putmalk: tutorial
Aaannnndd we're done! Hopefully that wasn't so hard.

=== Testing In-Game ===
Because we hooked our function into Lua, we can test it in game.
Load your mod and remember to set VFS to True for your DLL. Open up the FireTuner and load the game up. Create an archer, and select your Warrior. Enter the following commands into the tuner:
Code:
unit = UI.GetHeadSelectedUnit();
unit:GetMana();
The game should print out "100".
Select your archer.
Code:
unit = UI.GetHeadSelectedUnit();
unit:GetMana();
The game should print out "47".
Spoiler :

Congratulations! You've now loaded your new column into the game via the DLL! Now mess around! The possibilities are endless!