[PYTHON] expose DLL function (city) from to python

keldath

LivE LonG AnD PrOsPeR
Joined
Dec 20, 2005
Messages
7,362
Location
israel
hi all,

i want top use a function i added to the cycity in the maininterface.py file.

i thought i should do something like CyCity.myfunction().
but it didnt work and i got py error.

can someone give me pointers on how to call dll functions to the python?


thanks
 
You need to add the function to CyCity.h and CyCity.cpp. While it might seem like a gigantic class, CyCity is really only a CvCity pointer as the only variable. It has a lot of functions though, all calling CvCity functions using the pointer.

You then need to add the function to CyCityInterface.cpp.
PHP:
.def("kill", &CyCity::kill, "void () - kill the city")
First is what to call it in python. The second is a pointer to a C++ function. Last is a string, which I think is for debugging purposes. Not sure. I never used that string.

There is an optional argument telling python how to handle a new object, like if you request a new CyCity object, python needs to know python should remove it from memory once it's done using it while if it gets a pointer to CvTerrainInfo (just an example), then it should leave it in memory because the DLL still need to keep it around. In most cases you won't need this as you only need a primitive like int getFunc().

The reason why there are multiple interface files is because boost takes care of the interface and our ancient version of boost has a max 64 kB limit to how much code can be in a single file. If you need to add more interface files (really rarely needed), CvDllPython.cpp needs to be updated to call the new file during init.
 
hello again nightinggale,

thank you for replying :)

CyCityInterface.cpp -> ok i see, that was my missing link.
i saw most calls in the maininterface got through that,
but i thougnt i can just call CyCity.myfunction(). at first.

CyCity is really only a CvCity pointer
yes, f1rpo was kind to explain this for me.

python should remove it from memory once it's done
how would you suggest in handling this?
my func gets the amount of wonders built in a city in city screen, just displays info.

The reason why there are multiple interface files is because boost takes care of the interface and our ancient version of boost has a max 64 kB limit to how much code can be in a single file. If you need to add more interface files (really rarely needed), CvDllPython.cpp needs to be updated to call the new file during init.
yeah well, 16 years of civ4 ancient tech...


thank you very much !
i will try to play with this,
first time i do modding of python with c++.
 
Python interface return policy.

Copy by value (such as int getNum())
No return policy needed. You will get a copy in python while the original in C++ is gone. No risk of memory leaks.

Pointer to existing object
PHP:
.def("getCivicInfo", &CyGlobalContext::getCivicInfo, python::return_value_policy<python::reference_existing_object>(), "(CivicID) - CvInfo for CivicID")
PHP:
CvCivicInfo* CyGlobalContext::getCivicInfo(int i) const
{
   return &GC.getCivicInfo((CivicTypes) i);
}
Tell python that once it's done using the pointer, leave it in memory. Needed as C++ will still have a pointer to it.

Pointer to new object
PHP:
.def("getCapitalCity", &CyPlayer::getCapitalCity, python::return_value_policy<python::manage_new_object>(), "CyCity* (int iID)")
PHP:
CyCity* CyPlayer::getCapitalCity()
{
   return m_pPlayer ? new CyCity(m_pPlayer->getCapitalCity()) : NULL;
}
C++ allocates a new object and gives the pointer to python without storing the pointer itself. python::manage_new_object will then tell python to free the memory once python is done using it.
 
hey,
ok,
i guess for fetching the type of building, i can use the Pointer to new object.
my func is similar to .def("isWorldWondersMaxed", &CyCity::isWorldWondersMaxed, "bool ()")

thank you for the details friend.

edit:

still havent had success im afraid,
im using advc/kmod/bug
and the cycityinterface is splitted to 2 files.
cycityinterface1.cpp
cycityinterface2.cpp

i added this line in maininterface.py:

screen.setLabel("WonderLimit", "Background", localText.getText("TXT_KEY_CONCEPT_WONDER_LIMIT", (CyCityInterface2().isCultureWorldWondersMaxed())), CvUtil.FONT_CENTER_JUSTIFY, 125, self.yResolution - 234, -0.1, FontTypes.SMALL_FONT, WidgetTypes.WIDGET_GENERAL, -1, -1)


i tried - CyCityInterface2().isCultureWorldWondersMaxed(),
CyInterface().isCultureWorldWondersMaxed().

ok..
git it - CyCity.isCultureWorldWondersMaxed() :)
 
Last edited:
i guess for fetching the type of building, i can use the Pointer to new object.
my func is similar to .def("isWorldWondersMaxed", &CyCity::isWorldWondersMaxed, "bool ()")
Usually you would need to return an int for the building ID. If you then need information about that building, you can use gc.getBuildingInfo(building ID).
my func gets the amount of wonders built in a city in city screen, just displays info.
Sounds like you should count in C++ and return an int.

I see no reason to add the complexity of creating new object pointers for this task. That would make it overly complex. Also do as much as possible in C++ as it runs faster than python.
 
hey,
alright,
i got all i wanted. my addition works nicely.
thank you!

and yes,
im aware of speed of py vs c++,
i wrote alll the code in c++, just needed it to show on the UI.


one more if i may,
can i get hover test on the screen.setLabel(...) i added? i guess yes, but didnt see examples other than this fn screen.setHitTest(...)
 
can i get hover test on the screen.setLabel(...) i added? i guess yes, but didnt see examples other than this fn screen.setHitTest(...)
There are a few ways to do this. If you code a specific screen in python:
PHP:
    def getWidgetHelp(self, argsList):
       iScreen, eWidgetType, iData1, iData2, bOption = argsList
Return the text you want to show up.

Alternatively you can use C++ where you mod CvDLLWidgetData:: ParseHelp. This way you can make it more global as in not linked to a specific python screen. You can add widgets to the enum and that way make a type only you use and then set the help text if it is that widget type.

Important: do not change order of vanilla widgets in the enum. They are hardcoded in the exe. If you want to add your own, add them to the end of the enum and it will work. Enums can be exposed to python in CyEnumInterface.cpp. Look for
PHP:
python::enum_<WidgetTypes>("WidgetTypes")
 
hi,
great thank you .

i used the c++ part first, thanks for the heads up.
added in a nice parse text and all.
really nice !

thank you again , as usual for your professional answers.

result:

upload_2021-4-25_22-20-17.png
 
Last edited:
Top Bottom