jdog5000
Revolutionary
Background:
For my forth coming Tech Cost mod, I wanted to create a second cost value for Techs. This modified cost would then go down as more civilizations acquired the tech with other factors as well, with the modifications done in Python for easier tweaking. Anyway, to do this I needed to create some new CvInfos functions and expose them to Python. If you want to check out the mod, see TechCost Mod.
Declare Yourself
Ofcourse, to create new functions and expose them to Python, you have to declare them in the header file and implement them in the appropriate cpp file. Skip to next section if this is old hat for you.
For my mod, I added the following function declarations to CvInfos.h in the CvTechInfo class:
(EDIT: As pointed out by Gerikes below, the DllExport is not needed to expose these functions to Python, it exposes them to the game exe ... so it's harmless, but unnecessary)
// ---------------- TECH_COST_MOD start -----------------------
DllExport int getOrigResCost() const; // Exposed to Python
DllExport void setModResCost( const int newModCost ); // Exposed to Python
// ---------------- TECH_COST_MOD end -----------------------
You would add your function declarations in the appropriate .h file, and you should get in the habit of marking your changes very clearly so that you and others can find them. Makes merging much easier! This is classic C++ declaration, except for the DllExport command at the beginning of the line, which I believe just indicates the function should be exposed in the dll. The Python binding is done in a different file.
You ofcourse also implement these new functions in the cpp file associated with the class:
// ---------------- TECH_COST_MOD start -----------------------
// This function returns the unmodified research cost, will be exposed to Python
int CvTechInfo::getOrigResCost() const
{
return m_iResearchCost;
}
// Function that will be exposed to python to allow changes to cost of techs
// during game play
void CvTechInfo::setModResCost( const int newModCost )
{
m_iModResCost = newModCost;
}
// ---------------- TECH_COST_MOD end -------------------------
Expose Yourself
This is the somewhat tricky part. The exposure to Python is done in file with a name like Cy____Interface#.cpp, where the blank is filled by a word or too also in the name of the cpp file where your functions are. The # is a small integer, 1-3 and also may not be there for your particular file. For the Tech Cost mod, the file name is CyInfoInterface1.cpp. All the CvTechInfo bindings occur in this file, though other info class bindings are in CyInfoInterface2.cpp, etc. Not sure where to put it? Try a search in files for some already Python exposed function name (leave off ()!) in the class you're adding to.
(Note: see Gerikes post below for more information ... some Cv___.cpp files have a Cy___.cpp version as well, where you will have to create a wrapper for your Cv___.cpp function.)
Alright, here are the additions for the Tech Cost mod:
// ---------------- TECH_COST_MOD start -----------------------
.def("getOrigResCost", &CvTechInfo::getOrigResCost, "int ()")
.def("setModResCost", &CvTechInfo::setModResCost, "void (int newModCost)" )
// ---------------- TECH_COST_MOD end -----------------------
The three fields are:
1) The text of your new function name, as it will appear in Python.
2) Address of the new function (class::function).
3) Return type of your function and arguments it takes in
Compile your source code in Final Release (or maybe just Release) mode, then place the newly created CvGameCoreDLL.dll in your mods Assets folder. Congradulations, your new functions are exposed to Python!
How to use your new function
The interesting part comes in using your new functions in Python. There is a slight pitfall here to avoid. Your functions have been added to CyGlobalContext, and you have to refer to them using this. Many of the other functions for a particular class are essetially wrapped in PyHelpers.py but yours will not be. (Note: you could add yours to PyHelpers.py, but it doesn't appear to me that this loads with your mod, so you'd have to change the original ... bad! Comment if this is incorrect and I'll change this text)
Most of the .py files have a line at the beginning that reduces the amount of typing you have to do later:
gc = CyGlobalContext()
With this at the top of your python file, calls to your new functions should look something like:
OrigCost = gc.getTechInfo(techID).getOrigResCost()
gc.getTechInfo(techID).setModResCost( 12 )
The dots take you from Global Context -> Class -> your function. Now that you can access your new functions, your imagination is the limit! A few pointers: comment your additions! do as much in Python as reasonable! when you release your brilliant mod, release the source code as well so people can include it in their ideas!
For my forth coming Tech Cost mod, I wanted to create a second cost value for Techs. This modified cost would then go down as more civilizations acquired the tech with other factors as well, with the modifications done in Python for easier tweaking. Anyway, to do this I needed to create some new CvInfos functions and expose them to Python. If you want to check out the mod, see TechCost Mod.
Declare Yourself
Ofcourse, to create new functions and expose them to Python, you have to declare them in the header file and implement them in the appropriate cpp file. Skip to next section if this is old hat for you.
For my mod, I added the following function declarations to CvInfos.h in the CvTechInfo class:
(EDIT: As pointed out by Gerikes below, the DllExport is not needed to expose these functions to Python, it exposes them to the game exe ... so it's harmless, but unnecessary)
// ---------------- TECH_COST_MOD start -----------------------
DllExport int getOrigResCost() const; // Exposed to Python
DllExport void setModResCost( const int newModCost ); // Exposed to Python
// ---------------- TECH_COST_MOD end -----------------------
You would add your function declarations in the appropriate .h file, and you should get in the habit of marking your changes very clearly so that you and others can find them. Makes merging much easier! This is classic C++ declaration, except for the DllExport command at the beginning of the line, which I believe just indicates the function should be exposed in the dll. The Python binding is done in a different file.
You ofcourse also implement these new functions in the cpp file associated with the class:
// ---------------- TECH_COST_MOD start -----------------------
// This function returns the unmodified research cost, will be exposed to Python
int CvTechInfo::getOrigResCost() const
{
return m_iResearchCost;
}
// Function that will be exposed to python to allow changes to cost of techs
// during game play
void CvTechInfo::setModResCost( const int newModCost )
{
m_iModResCost = newModCost;
}
// ---------------- TECH_COST_MOD end -------------------------
Expose Yourself
This is the somewhat tricky part. The exposure to Python is done in file with a name like Cy____Interface#.cpp, where the blank is filled by a word or too also in the name of the cpp file where your functions are. The # is a small integer, 1-3 and also may not be there for your particular file. For the Tech Cost mod, the file name is CyInfoInterface1.cpp. All the CvTechInfo bindings occur in this file, though other info class bindings are in CyInfoInterface2.cpp, etc. Not sure where to put it? Try a search in files for some already Python exposed function name (leave off ()!) in the class you're adding to.
(Note: see Gerikes post below for more information ... some Cv___.cpp files have a Cy___.cpp version as well, where you will have to create a wrapper for your Cv___.cpp function.)
Alright, here are the additions for the Tech Cost mod:
// ---------------- TECH_COST_MOD start -----------------------
.def("getOrigResCost", &CvTechInfo::getOrigResCost, "int ()")
.def("setModResCost", &CvTechInfo::setModResCost, "void (int newModCost)" )
// ---------------- TECH_COST_MOD end -----------------------
The three fields are:
1) The text of your new function name, as it will appear in Python.
2) Address of the new function (class::function).
3) Return type of your function and arguments it takes in
Compile your source code in Final Release (or maybe just Release) mode, then place the newly created CvGameCoreDLL.dll in your mods Assets folder. Congradulations, your new functions are exposed to Python!
How to use your new function
The interesting part comes in using your new functions in Python. There is a slight pitfall here to avoid. Your functions have been added to CyGlobalContext, and you have to refer to them using this. Many of the other functions for a particular class are essetially wrapped in PyHelpers.py but yours will not be. (Note: you could add yours to PyHelpers.py, but it doesn't appear to me that this loads with your mod, so you'd have to change the original ... bad! Comment if this is incorrect and I'll change this text)
Most of the .py files have a line at the beginning that reduces the amount of typing you have to do later:
gc = CyGlobalContext()
With this at the top of your python file, calls to your new functions should look something like:
OrigCost = gc.getTechInfo(techID).getOrigResCost()
gc.getTechInfo(techID).setModResCost( 12 )
The dots take you from Global Context -> Class -> your function. Now that you can access your new functions, your imagination is the limit! A few pointers: comment your additions! do as much in Python as reasonable! when you release your brilliant mod, release the source code as well so people can include it in their ideas!