1. We have added a Gift Upgrades feature that allows you to gift an account upgrade to another member, just in time for the holiday season. You can see the gift option when going to the Account Upgrades screen, or on any user profile screen.
    Dismiss Notice

Quick Modding Questions Thread

Discussion in 'Civ4 - Creation & Customization' started by kiwitt, Jan 27, 2010.

  1. hommar

    hommar Chieftain

    Joined:
    Jul 9, 2017
    Messages:
    3
    Hi.
    I try to add sort of random event, but can't get it to work.
    I want [unit] to randomly spawn in cities with [building]. Additionaly the new unit is supposed to recive [promotion1] when [building1] is present in the spawning city. Adding promotins 'might' work, but spawning does not.
    This is pretty much the same as 'Sheaim' civilization's 'Planar Gate' from 'RIFE' (if anyone knows the mod). I just can't find this script in python files to compare with my scribbles...
    There's one more thing i can think of, that i could (should?) add to the script, but are not sure if it will help.
    I'll be grateful for any help with this. A to-do checklist maybe? If someone would be willing to give me larger scale aid, i can send the entirity of my scribbles.
    Yes, I am totally green in modding...
     
  2. Nightinggale

    Nightinggale Deity

    Joined:
    Feb 2, 2009
    Messages:
    4,561
    First you need to set up an event trigger condition. Sounds like all you need to do is to set the tag BuildingsRequired and it should trigger only in cities with the selected building class.

    I have no idea what you have done, but it sounds fairly strait forward to me. Make a python callback in the event. The events are in python/EntryPoints/CvRandomEventInterface.py

    Look at applyTsunami1
    PHP:
    def applyTsunami1(argsList):
       
    iEvent argsList[0]
       
    kTriggeredData argsList[1]
      
       
    player gc.getPlayer(kTriggeredData.ePlayer)
       
    city player.getCity(kTriggeredData.iCityId)
      
       
    city.kill()
    Obviously you don't need the last line as it kills the city, but the rest gives you what you need: kTriggeredData, player and city.

    The full list of variables is in CyStructInterface1.cpp (yes the DLL code, but you don't have to compile it to just read lists like this)
    Spoiler :
    PHP:
        python::class_<EventTriggeredData>("EventTriggeredData")
           .
    def_readwrite("iId", &EventTriggeredData::m_iId)
           .
    def_readwrite("eTrigger", &EventTriggeredData::m_eTrigger)
           .
    def_readwrite("iTurn", &EventTriggeredData::m_iTurn)
           .
    def_readwrite("ePlayer", &EventTriggeredData::m_ePlayer)
           .
    def_readwrite("iCityId", &EventTriggeredData::m_iCityId)
           .
    def_readwrite("iPlotX", &EventTriggeredData::m_iPlotX)
           .
    def_readwrite("iPlotY", &EventTriggeredData::m_iPlotY)
           .
    def_readwrite("iUnitId", &EventTriggeredData::m_iUnitId)
           .
    def_readwrite("eOtherPlayer", &EventTriggeredData::m_eOtherPlayer)
           .
    def_readwrite("iOtherPlayerCityId", &EventTriggeredData::m_iOtherPlayerCityId)
           .
    def_readwrite("eReligion", &EventTriggeredData::m_eReligion)
           .
    def_readwrite("eCorporation", &EventTriggeredData::m_eCorporation)
           .
    def_readwrite("eBuilding", &EventTriggeredData::m_eBuilding)

    Now you need to create a new unit using the player interface:
    PHP:
    .def("initUnit", &CyPlayer::initUnitpython::return_value_policy<python::manage_new_object>(), "CyUnit* initUnit(UnitTypes iIndex, plotX, plotY, UnitAITypes iIndex)  - place Unit at X,Y   NOTE: Always use UnitAITypes.NO_UNITAI")
    I read it as you need to write:
    PHP:
    unittype gc.getDefineINT("UNIT_TYPE"# obviously use the type from xml
    unit player.initUnit(unittypekTriggeredData.plotXkTriggeredData.plotYUnitAITypes.NO_UNITAI)
    Next add the promotion if the city has the building in question:
    PHP:
    building gc.getDefineINT("building1")
    if 
    city.isHasBuilding(building):
        
    promotion1 gc.getDefineINT("PROMOTION_promotion1")
        
    unit.setHasPromotion(promotion1)
    I haven't tested this, but I think that's how to do it, or at least one way to do it ;)
     
  3. hommar

    hommar Chieftain

    Joined:
    Jul 9, 2017
    Messages:
    3
    As for 'add promotion', it seems like i allready did it right.
    I also use the
    if CyGame().getSorenRandNum(100, "SpawnMbomb") <= iSpawnChance: - not sure, but 'spawnmbomb' (mbomb is the name of my unit) seems to be for ease of read only, that's the feeling i get from reading different scripts.
    line to make it random. Do i need it here as well?

    kTriggeredData = argsList[1]
    What is 'argsList' abaut? Like does it use a 'premade' list, or something, or the lines below make the list?

    .def("initUnit", &CyPlayer::initUnit, python::return_value_policy<python::manage_new_object>(), "CyUnit* initUnit(UnitTypes iIndex, plotX, plotY, UnitAITypes iIndex) - place Unit at X,Y NOTE: Always use UnitAITypes.NO_UNITAI")
    Not sure how much i understand here... I truly feel dumb looking at this line. Everything below is quite simple even for me.

    As for now, it seems like my worst mistake was not making any event triggers at all... Never played with them before, time to change that.

    Thank You very much, I'd still know nothing if it wasn't for you.
    Have a nice day. :)
     
  4. Nightinggale

    Nightinggale Deity

    Joined:
    Feb 2, 2009
    Messages:
    4,561
    Whenever the DLL file makes a call to python, it generates a list (array) of arguments. Simply put, it's a list of variables the DLL wants to give to python. There are multiple locations in the DLL, which calls python and each tend to have a unique setup of arguments. However it's less tricky than it sounds because vanilla gets the arguments right. This means you can look up some vanilla event, which has a python callback and then you copy paste the argsList code from that one. I picked applyTsunami1, though any event with a python callback could be used for this.

    The contents of EventTriggeredData is a bit more tricky. Experience made me look it up in CyStructInterface1.cpp, though I also read CvStructs.h too.

    All the game data is in the DLL. This means when you run code in python, you need to call DLL functions to read and modify the game data. This is done by getting a pointer to an object and then call member functions.

    Luckily it's not as tricky as it sounds. Take for instance:
    PHP:
    player gc.getPlayer(kTriggeredData.ePlayer)
    gc is CyGlobalContext, which is an object, which is always available.
    getPlayer is a member function of CyGlobalContext. It returns a CyPlayer object, which is stored in player. player can then use the member functions for CyPlayer.

    The member functions are listed in Cy*Interface*.cpp. The first * is the object (like player) and the last is a number (which can be skipped). Due to a compiler limitation, the interface files can't exceed 64 kB, which is why some of them have the functions split into multiple files.

    In the interface files, each member function is defined in a single line.
    PHP:
    .def("initUnit", &CyPlayer::initUnitpython::return_value_policy<python::manage_new_object>(), "CyUnit* initUnit(UnitTypes iIndex, plotX, plotY, UnitAITypes iIndex)  - place Unit at X,Y   NOTE: Always use UnitAITypes.NO_UNITAI")
    It's in CyPlayer, which means it's a function, which can be used by player objects.
    1. "initUnit"
      is the name of the function, as in what you need to write in python
    2. &CyPlayer::initUnit
      is the name of the function inside the DLL to call. You can look up the function if you like
    3. python::return_value_policy<python::manage_new_object>()
      is return policy as in memory management. Forget about this unless you mod the DLL interface itself
    4. "CyUnit* initUnit(UnitTypes iIndex, plotX, plotY, UnitAITypes iIndex) - place Unit at X,Y NOTE: Always use UnitAITypes.NO_UNITAI"
      the last is always a comment. It tells how to use the function
    Figuring out how to read the python interface in the DLL should help a lot when writing python code. People talk about python exposed functions and not exposed functions. The functions in the interface are the exposed functions while the non-exposed are those present in the DLL, but aren't mentioned in the interface files, meaning python can't access them.
     
  5. hommar

    hommar Chieftain

    Joined:
    Jul 9, 2017
    Messages:
    3
    After reading your post i looked back on my code, and realised it had no chances of working as intended. :)

    If i understand it correctly, i'll have to find out wich argslist fit my needs.
    My current scribble is in spoiler, if You want to see how bad it is. :)
    Spoiler code :

    def SpawnMbomb(argsList):
    iEvent = argsList[0]
    kTriggeredData = argsList[1]

    pPlayer = gc.getPlayer(kTriggeredData.ePlayer)
    pCity = pPlayer.getCity(kTriggeredData.iCityId)
    iSpawnChance = 100

    if pCity.getNumRealBuilding(gc.getInfoTypeForString('BUILDING_MBOMBMOD1')) > 0:
    iPromotion = gc.getInfoTypeForString('PROMOTION_MBOMBMOD1')
    if pCity.getNumRealBuilding(gc.getInfoTypeForString('BUILDING_MBOMBMOD2')) > 0:
    iPromotion = gc.getInfoTypeForString('PROMOTION_MBOMBMOD2')

    if pCity.getNumRealBuilding(gc.getInfoTypeForString('BUILDING_MANABOMBWORKSHOP')) > 0:
    if CyGame().getSorenRandNum(100, "SpawnMbomb") <= iSpawnChance:
    iMbomb = gc.getInfoTypeForString('UNIT_MBOMB')
    newUnit = pPlayer.initUnit(iMbomb, pCity.getX(), pCity.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_SOUTH)
    newUnit.setHasPromotion(iPromotion, True)

    You're more than helpful to me, if it means anything. :)
    -- Nevermind, i just reread what You wrote abaut argslists, id does't seem like this is any close to proper code. --
     
    Last edited: Jul 11, 2017
  6. ChaosSlayer

    ChaosSlayer King

    Joined:
    Feb 3, 2011
    Messages:
    608
    Question : where can I find how to adjust city maintence?
    I found the distance modifier in global defines

    <Define>
    <DefineName>MAX_DISTANCE_CITY_MAINTENANCE</DefineName>
    <iDefineIntVal>25</iDefineIntVal>
    </Define>

    However is there anywhere I can find the modifier for number of cities?
     
  7. Dancing Hoskuld

    Dancing Hoskuld Deity

    Joined:
    Jul 5, 2004
    Messages:
    23,544
    Gender:
    Male
    Location:
    Canberra, Australia
    There is one set in the Handicap and another in the WorldInfo files in the XML/GameInfo folder
     
  8. ChaosSlayer

    ChaosSlayer King

    Joined:
    Feb 3, 2011
    Messages:
    608
    Thank You! that surely will be helpful ;)
     
  9. Leoreth

    Leoreth Prince of Blood Moderator

    Joined:
    Aug 23, 2009
    Messages:
    34,953
    Gender:
    Male
    Location:
    House of Hades
    What are acceptable polygon counts for building models? I'm currently browsing Google 3D Warehouse, but all the polygon counts seem very daunting to me.
     
  10. SaibotLieh

    SaibotLieh Emperor

    Joined:
    Sep 25, 2009
    Messages:
    1,524
    The more common the building will be the less polygons it should have. I think around 1000 polygons is a good number to aim for, but you should be able to go higher for wonders and such.
     
  11. Leoreth

    Leoreth Prince of Blood Moderator

    Joined:
    Aug 23, 2009
    Messages:
    34,953
    Gender:
    Male
    Location:
    House of Hades
    Alright, thanks. I'm looking for wonders, but I guess that rules out the ~20k models I've seen :)
     
  12. Dancing Hoskuld

    Dancing Hoskuld Deity

    Joined:
    Jul 5, 2004
    Messages:
    23,544
    Gender:
    Male
    Location:
    Canberra, Australia
    Is there a way to change the default start era in the Custom Game screen?

    The two mods I know of that add eras before Ancient have left the first era as ERA_ANCIENT and just changed its the displayed text to the earlier era and used another era for the ancient era.
     
  13. Nightinggale

    Nightinggale Deity

    Joined:
    Feb 2, 2009
    Messages:
    4,561
    GlobalDefines contains this, though I haven't tried changing it.
    PHP:
    <Define>
        <
    DefineName>STANDARD_ERA</DefineName>
        <
    DefineTextVal>ERA_ANCIENT</DefineTextVal>
    </
    Define>
    The start era is also mentioned in the ini file, which makes me wonder if it is a good idea not to have the same in all mods.
     
  14. Dancing Hoskuld

    Dancing Hoskuld Deity

    Joined:
    Jul 5, 2004
    Messages:
    23,544
    Gender:
    Male
    Location:
    Canberra, Australia
    Tried that but it had no affect. :(
     
  15. Nightinggale

    Nightinggale Deity

    Joined:
    Feb 2, 2009
    Messages:
    4,561
    I just did a quick search of the exe file. It contains the string ERA_ANCIENT, meaning this could be hard coded.
     
  16. need my speed

    need my speed Rex Omnium Imperarium

    Joined:
    Oct 3, 2009
    Messages:
    2,181
    Gender:
    Male
    Location:
    European Union (Magna Batavia)
    Why are the player colours of my new civilisations not working? I get errors like these:



    But, what does that even mean? All my code looks just like any other of the dozens of civilisations I have added:



     
  17. Nightinggale

    Nightinggale Deity

    Joined:
    Feb 2, 2009
    Messages:
    4,561
    This error originates from CvXMLLoadUtility::FindInInfoClass, which in turn relies on GC.getInfoTypeForString. This one is a map (aka hash table), which contains all Type tags read until this point (the read code sets it) and it will return the index. The error occurs when the string isn't found. In other words this can only happen if the file with the Type in question is not read.

    The next question is why is the file not read? One answer is file reading order, but since vanilla can read colors for civs, I assume that's not the case without actually checking (you can read the order in CvXMLLoadUtilitySet.cpp, though the list is spread across 7 functions, hence fragmented).

    The next thing, which comes to mind is that there is some error in the player color file, which prevents it from reading the string, though I have no idea what it could be. It would be interesting to try to load with a debug DLL to see what it will assert on. If the assert approach fails, the only alternative I can think of is to debug and set a breakpoint when it loads the player color to step through and see what it does.

    edit: try using Notepad++ on the xml files. Go into top menu->view->Show Symbols->Show All Characters. With this on, you will be able to detect if your xml file for some odd reason has ended up with an invisible character, either in Type or in PlayerColor, which will prevent them from being the same.
     
    need my speed likes this.
  18. need my speed

    need my speed Rex Omnium Imperarium

    Joined:
    Oct 3, 2009
    Messages:
    2,181
    Gender:
    Male
    Location:
    European Union (Magna Batavia)
    That is an extremely detailed answer!

    I have never been able to successfully compile any kind of .dll, nor have I ever changed it - well, aside from using the 50+ civilisations one - so I will assume that .cpp is unchanged... If I can make that assumption?

    With the debug .dll, do you mean enabling LoggingEnabled and the like, in CivilizationIV.ini?

    I saw nothing out of the ordinary, but I did notice one new thing upon launching the game. I added SMAC's seven civilisations, and the player colour error messages are in the order of the player colour's appearance - but there is an eighth one about the Lord's Believers' colour (which is also the sixth one, as it should be; the civilisations are in the order of Sparta, Gaia, University, Peacekeepers, Hive, Lord, Morgan). I see nothing odd that could explain this though; there is only one single reference to this colour in CIV4CivilizationInfos.xml, and only one such colour in CIV4PlayerColorInfos.xml.

    Pseudo-EDIT: Actually, I rewrote the entire thing, and put the new colours somewhere in the middle, and now it works... So far.

    Well. Thank you for your help, really. It was a very detailed and informed answer. :)
     
  19. need my speed

    need my speed Rex Omnium Imperarium

    Joined:
    Oct 3, 2009
    Messages:
    2,181
    Gender:
    Male
    Location:
    European Union (Magna Batavia)
    One week later and one question more!

    Could someone confirm whether this resource works: https://forums.civfanatics.com/resources/byzantine-greek-flamethrower.15680/

    Specifically, when I try to use it, I get something similar to this: https://forums.civfanatics.com/threads/help-with-empty-civilopedia.383539/#post-9670976

    The Civlopedia page only shows the unit's button. In-game, when I select the unit and attack with it, the text pop-ups ('99% of victory' and such) disappear, and entering and exiting the city interface doesn't actually close the city interface.
     
  20. pichulo_

    pichulo_ Chieftain

    Joined:
    Aug 23, 2017
    Messages:
    14
    Gender:
    Male
    Hello! I'm trying to figure out if it is possible to rename your cities with more than 15 characters, I was looking through the xml files and couldn't find anything. Any help please? Thanks a lot!
     

Share This Page