Quick Modding Questions Thread

If you want to add a building which can only be built by a unit (like the Academy etc) and a new unit to be that builder (and it does nothing else, including combat), under <DefaultUnitAI> in the unit xml file, what do you put to make sure the ai knows how to use it? All the units in the game that build buildings do something else as well, and since this unit wouldn't be able to do any of those things, I'm worried that giving it one of the existing unit ais might confuse it.
 
If you want to add a building which can only be built by a unit (like the Academy etc) and a new unit to be that builder (and it does nothing else, including combat), under <DefaultUnitAI> in the unit xml file, what do you put to make sure the ai knows how to use it? All the units in the game that build buildings do something else as well, and since this unit wouldn't be able to do any of those things, I'm worried that giving it one of the existing unit ais might confuse it.
AFAIK The problem is not whether the AI will use the unit to build that building. The problem is: Will the AI train the unit.
To my experience the AI won't understand that in order to get the building it needs a unit.
 
AFAIK The problem is not whether the AI will use the unit to build that building. The problem is: Will the AI train the unit.
To my experience the AI won't understand that in order to get the building it needs a unit.
Wouldn't giving the unit high flavours in the code make it do that?

EDIT: Oh, looking at the other units, none of them seem to use the flavor tag. Why on Earth did they add a flavour tag to units if they weren't going to use it? And how does the AI know to build any units?
 
Last edited:
I'm trying to make it so I don't have to write all the "globals"
1734988852231.png

1734988861971.png


1734988868430.png
 
@Darkator
Hi,
I happened to stamle on your post above.
The code is highly in efficient .
You dont have to do the same loop over and over.

sudo code example:
Python:
def country(pcountry):
     imaxciv = ...
   i_ussr = ...
   i_sourh_africa = ...
   i_germany = ...
  civtype = pplayer.getcivilizationtype()
     for icivs in range(imaxciv):
     pplayer = gc,getplayer(icivs)
    if pcountry == 1:
            if civtype == i_ussr:
               return icivs

add the code above as code so i can copy it and ill rewrite it for you.
also chatgpt can do the same for you.

being efficient in python code for civ4 is very important for game speed.

cheers
 
Last edited:
do you know how to make an event in python with selection

I wanted to make an event with selecting the "escalation level" (a window with three options to choose from at the very beginning of the game) and another with selecting the "year of war" (a window, a window similar only after this), there is no such thing as a little totorial with things like civ4
 
getInfoTypeForString is supposed to map a <Type> string from XML - like "UNIT_AXEMAN" - to a DLL-internal ID. Usually gets called by Python code, for example here by the WBSave parser to look up "CIVIC_..." strings found in a WBSave file. Apart from civics, the parser looks up state religions and start eras. So maybe it's worth checking whether your WBSave assigns empty strings to any of those. Oh, but the parser also looks up lots of other type strings via findInfoTypeNum in CvUtil.py:
Spoiler :
Code:
def findInfoTypeNum(infoGetter, numInfos, typeStr):
    if (typeStr == 'NONE'):
        return -1
    idx = gc.getInfoTypeForString(typeStr)
    pyAssert(idx != -1, "Can't find type enum for type tag %s" %(typeStr,))
    return idx
So any line with ...Type=(nothing) could be responsible. Maybe the problem will become more apparent if you just ignore the failed assertion? Anything in PythonErr.log if you do so? It might also help to adopt this bugfix to the pyAssert function. That function is supposed to write a Python stacktrace to PythonErr.log when findInfoTypeNum fails, but it's incorrectly implemented and just silently ignores the failure instead. A more reliable way to get a Python exception would be to (temporarily) change getInfoTypeForString in the DLL so that it crashes the DLL rather than show a failed-assertion popup. But that will require recompilation of the DLL.
Thank you. for solve for my error.
 
I have a not serious but slightly annoying bug when pressing ALT+TAB and coming back:

1736293233889.png


This is what CvEnhancedTechConquestEventManager.py contains:

Python:
## Sid Meier's Civilization 4
## Copyright Firaxis Games 2005

from CvPythonExtensions import *
import CvUtil
import CvEventManager
import sys
import EnhancedTechConquest
    
gc = CyGlobalContext()

enhancedTechConquest = EnhancedTechConquest.EnhancedTechConquest()

# globals
###################################################
class CvEnhancedTechConquestEventManager:
    def __init__(self, eventManager):
        eventManager.addEventHandler("cityAcquired", self.onCityAcquired)
        eventManager.addEventHandler("windowActivation", self.onWindowActivation)
    
        EnhancedTechConquest.loadConfigurationData()

    def onCityAcquired(self, argsList):
        enhancedTechConquest.onCityAcquired(argsList)
        
        
    def onWindowActivation(self, argsList):
        'Called when the game window activates or deactivates'
        bActive = argsList[0]
        
        if(bActive):
            EnhancedTechConquest.loadConfigurationData()

Any suggestions?
 
Can't say that I understand the nature of the error message. Seems to come from the C++/Python integration layer, to put it vaguely. If there's a Python exception in loadConfigurationData (onWindowActivation itself looks correct), you'd think there'd be a Python stack trace with line numbers. Maybe window activation is handled in a separate thread and that somehow interferes with proper error reporting. Perhaps printing to PythonDbg.log at least will work - so inserting some (more) print statements could make clearer where the problem occurs. Also conceivable, however, that the event handler isn't even successfully getting called.

I wonder why the configuration data needs to be reloaded each time the window activates. I suspect that this was only added as a convenience for testing. If you can verify that the config gets loaded correctly when CvEnhancedTechConquestEventManager.__init__ is called, then you could perhaps just remove the addEventHandler call for window activation. (I guess you could just remove it and then check whether TechConquest.ini is getting ignored. I expect that, in that case, g_iBasePercent and g_iPopPercent in EnhancedTechConquest will remain at their initial values of 0 and 10 respectively.)
 
Can't say that I understand the nature of the error message. Seems to come from the C++/Python integration layer, to put it vaguely. If there's a Python exception in loadConfigurationData (onWindowActivation itself looks correct), you'd think there'd be a Python stack trace with line numbers. Maybe window activation is handled in a separate thread and that somehow interferes with proper error reporting. Perhaps printing to PythonDbg.log at least will work - so inserting some (more) print statements could make clearer where the problem occurs. Also conceivable, however, that the event handler isn't even successfully getting called.

I wonder why the configuration data needs to be reloaded each time the window activates. I suspect that this was only added as a convenience for testing. If you can verify that the config gets loaded correctly when CvEnhancedTechConquestEventManager.__init__ is called, then you could perhaps just remove the addEventHandler call for window activation. (I guess you could just remove it and then check whether TechConquest.ini is getting ignored. I expect that, in that case, g_iBasePercent and g_iPopPercent in EnhancedTechConquest will remain at their initial values of 0 and 10 respectively.)

Okay, commenting out the last 6 lines seem to have solved it. The message doesn't come back and I hope nothing else will be broken either.
Thanks again!
 
Not getting my hopes up, but is there any way to render a graphical asset on a tile without it being part of a city, feature, bonus, or improvement? Assuming I have existing art e.g. for a building, can I put the art on the tile without creating a bespoke improvement (etc.) for it?
 
Maybe some mod already does this. I thought perhaps Rise of Mankind (AND), but it looks like that mod has only added such a tag to leader traits (and it does so by adding a tag TraitYieldChanges to ImprovementInfo).
Sorry for necroposting or if someone else has already mentioned this, but Realism Invictus did that long ago. Could you please highlight what parts are relevant for buildings affecting improvement yields? Here's the source code. I don't have a coding background and hasn't seen guides on where variables and other new "objects" should be declared except maybe in CvEnums, .h counterpart and maybe other mentioned Cvs, so maybe you could also clarify which them of would be needed aside from the loop part?

@Mario W have you already figured it out or maybe still interested as well?
 
I'm pleased to announce that c4lib, c4edit and c4recover are now available for download on github.

c4edit is a command line used to edit BTS saves, including compressed BTS data.

c4lib is a C++ library, allowing programmers to modify BTS saves.

c4recover is a command line program built using c4lib, which allows users to remove "Locked Modified Assets" from a save.

Of particular interest is c4edit. This command line utiliity offers the ability to view any BTS save in a human readable, text-format file. For example, c4edit can be used to view any BTS save in a debugger-like text format. An example of this format is as follows:

Code:
Savegame: Tiny-Map-BC-4000.CivBeyondSwordSave
Schema: BTS.schema
Date: 01-12-2025 19:59:33.508379 UTC
c4lib version: 01.00.00

Offset       Hex                                                         ASCII              Translation                                   
0x00000000 | -- -- -- --  | -- -- -- --  | -- -- -- --  | -- -- -- --  | ---------------- | Begin Savegame
0x00000000 | -- -- -- --  | -- -- -- --  | -- -- -- --  | -- -- -- --  | ---------------- |   Begin GameHeader
0x00000000 | 2e 01 00 00  | -- -- -- --  | -- -- -- --  | -- -- -- --  | ....------------ |     GameVersion=302
0x00000004 | 00 00 00 00  | -- -- -- --  | -- -- -- --  | -- -- -- --  | ....------------ |     RequiredMod=""
0x00000008 | 00 00 00 00  | -- -- -- --  | -- -- -- --  | -- -- -- --  | ....------------ |     ModMd5=""
0x0000000c | 00 00 00 00  | -- -- -- --  | -- -- -- --  | -- -- -- --  | ....------------ |     ChecksumDWord=0
0x00000010 | 00 00 00 00  | -- -- -- --  | -- -- -- --  | -- -- -- --  | ....------------ |     LockModifiedAssetsText=""
0x00000014 | 00 00 00 00  | -- -- -- --  | -- -- -- --  | -- -- -- --  | ....------------ |     LmaMd5_1=""
0x00000018 | 00 00 00 00  | -- -- -- --  | -- -- -- --  | -- -- -- --  | ....------------ |     LmaMd5_2=""
0x0000001c | 00 00 00 00  | -- -- -- --  | -- -- -- --  | -- -- -- --  | ....------------ |     LmaMd5_3=""
0x00000020 | 00 00 00 00  | -- -- -- --  | -- -- -- --  | -- -- -- --  | ....------------ |     LmaMd5_4=""
0x00000024 | bf 05 00 00  | -- -- -- --  | -- -- -- --  | -- -- -- --  | ....------------ |     CvInitCoreMd5Size=1471
0x00000028 | -- -- -- --  | -- -- -- --  | -- -- -- --  | -- -- -- --  | ---------------- |   End GameHeader
0x00000028 | -- -- -- --  | -- -- -- --  | -- -- -- --  | -- -- -- --  | ---------------- |   Begin CvInitCore
0x00000028 | 01 00 00 00  | -- -- -- --  | -- -- -- --  | -- -- -- --  | ....------------ |     SaveFlag=1
0x0000002c | 00 00 00 00  | -- -- -- --  | -- -- -- --  | -- -- -- --  | ....------------ |     Type=GAME_SP_NEW
0x00000030 | 10 00 00 00  | 50 00 61 00  | 73 00 73 00  | 65 00 6e 00  | ....P.a.s.s.e.n. |     GameName="Passenger's Game"
0x00000040 | 67 00 65 00  | 72 00 27 00  | 73 00 20 00  | 47 00 61 00  | g.e.r.'.s. .G.a. |     ...
0x00000050 | 6d 00 65 00  | -- -- -- --  | -- -- -- --  | -- -- -- --  | m.e.------------ |     ...

For programmers, c4lib offers the ability to edit any BTS save, including data in the zlib-compressed portion of a save.

As a simple demonstration of c4lib's capability, c4recover was built using the c4lib API. c4recover removes Locked Modifed Assets from a BTS save.

For further details, please refer to the civ4lib thread.

c4lib and c4edit can be downloaded on GitHub here (note: on the GitHub page the download link is the button to the right of the page called "Latest").
 
Last edited:
Back
Top Bottom