Quick Modding Questions Thread

I never understood the idea behind hiding python errors. If there is an error, then people will notice something if wrong, error message or no error message. Even worse, the default is to hide the errors and sometimes the ini file resets itself to hide (related to crashing game? switching active mod?).

I changed it to always be active after I missed a bug due to the hiding setting having reset itself without telling me. I added the following:

PHP:
DllExport bool CvXMLLoadUtility::LoadPlayerOptions()
{
   if (!CreateFXml())
       return false;
   gDLL->ChangeINIKeyValue("CONFIG", "HidePythonExceptions", "0");
Add the last line to the DLL and your mod will never again suffer from having the python errors hidden, regardless of user settings.
That's great! Can't even remember how often I had to do the song and dance of "do you have exceptions enabled" - "no I didn't even know what that was" - "then I cannot help you but please turn it on in case of future errors" because of this.

This game was released in the time of boxed DVD releases where the idea of reporting errors online and rolling out frequent patches wasn't really accepted yet. I assume they just decided to hide error messages from the user to make their product appear more functional because it was a lot less expected for users to see error messages and they didn't have a mechanism to act on those user reported errors anyway. Typical corporate decisionmaking.
 
@Jessie Kirk: CyPlot.getUnit returns a CyUnit object. I think

if pPlot.getUnit(i).getUnitType() == ...

should work.

Yes indeed, that fixed it! Thank you very much, indeed!

My code now looks like this:
Code:
def cannotTrain(self,argsList):
        pCity = argsList[0]
        eUnit = argsList[1]
        bContinue = argsList[2]
        bTestVisible = argsList[3]
        bIgnoreCost = argsList[4]
        bIgnoreUpgrades = argsList[5]

## New Code

        if eUnit == gc.getInfoTypeForString("UNIT_WARRIOR"):

            i = 0
            pPlot = pCity.plot()

            if pPlot.getNumUnits() > 0:

                for i in range(pPlot.getNumUnits()):

                    if if pPlot.getUnit(i).getUnitType() == gc.getInfoTypeForString("UNIT_WARRIOR"):

                        return True

                return False

### New Code End

        return False

NOTE: if another player's Warriors were occupying the plot the city is on, I imagine it would prevent that city from being able to build Warriors. Also, the player that controls the city could simply move the newly trained Warriors off the city and then would be able to build some more, and if Warriors were moved onto a city when it is training Warriors, it would probably result in the city being unable to continue producing a Warrior and replacing the lost production with gold.

I was just using Warriors as a stand-in; fortunately for my purposes, this script will run on a new unit where none of these events could occur. I just wanted to warn anyone who may wish to use my script to limit training units in general.
 
Yes indeed, that fixed it! Thank you very much, indeed!

My code now looks like this:
Code:
def cannotTrain(self,argsList):
        pCity = argsList[0]
        eUnit = argsList[1]
        bContinue = argsList[2]
        bTestVisible = argsList[3]
        bIgnoreCost = argsList[4]
        bIgnoreUpgrades = argsList[5]

## New Code

        if eUnit == gc.getInfoTypeForString("UNIT_WARRIOR"):

            i = 0
            pPlot = pCity.plot()

            if pPlot.getNumUnits() > 0:

                for i in range(pPlot.getNumUnits()):

                    if if pPlot.getUnit(i).getUnitType() == gc.getInfoTypeForString("UNIT_WARRIOR"):

                        return True

                return False

### New Code End

        return False

NOTE: if another player's Warriors were occupying the plot the city is on, I imagine it would prevent that city from being able to build Warriors. Also, the player that controls the city could simply move the newly trained Warriors off the city and then would be able to build some more, and if Warriors were moved onto a city when it is training Warriors, it would probably result in the city being unable to continue producing a Warrior and replacing the lost production with gold.

I was just using Warriors as a stand-in; fortunately for my purposes, this script will run on a new unit where none of these events could occur. I just wanted to warn anyone who may wish to use my script to limit training units in general.
Where does your code goes to? (I don't know much about python but can do some merging).
Your code seems to be just the answer for something I had in mind for a long time: Ion Cannon from Tiberian universe.
 
Where does your code goes to? (I don't know much about python but can do some merging).
Your code seems to be just the answer for something I had in mind for a long time: Ion Cannon from Tiberian universe.

It replaces the cannotTrain function in "CvGameUtils.py". That's located in "Assets/Python/".
 
Once again I am in need of assistance.

How do I change the position of the scoreboard?

Mod: Better BUG AI
file: CvMainInterface.py

This is what I try to do...
CvMainInterface.py.jpg


Spoiler Unnecassary Long Explanation :

1 - I cannot upload the copy of CvMainInterface.py I'm working on. Forum tells me it is a non-allowed filetype.
2 - You may have noticed I've successfully changed the size of the minimap (YAY!).
3 - I've been through the more-than-5000-line-file several times.
4 - Here is what I've done:

I searched the file for "ScoreBackground" and found:
line 1009 screen.addPanel
line 1010 screen.hide
line 4829 screen.hide
line 5170 screen.setPanelSize
line 5171 screen.show
line 5271 screen.hide
line 5313 screen.setPanelSize
line 5314 screen.show

I searched the file for "WidgetTypes.WIDGET_CONTACT_CIV"
and found it in lines 1014 and 5146.

I've changed the Y values for all lines that "screen.addPanel" and "screen.setPanelSize" that I found.

It doesn't matter if I change only one location, or all of them at the same time.
The scoreboard is stuck at approximately Y = 670 (in a 1440 x 900 gamewindow)

I need to move the scoreboard up to approx Y = 490 to get it above the larger minimap.

H...E...L...P...

I did spend the whole Sunday on this one. Reading through - and searching (CTRL + F) through - CvMainInterface.py in the [Better BUG AI/Assets/Python/Screens] folder. And making the - what I thought was - necessary changes. Nothing...

There is another file in the mod, though: CvMainInterface-PLE.py. I have not touched that one at all. I don't think it overrides CvMainInterface.py. If it did, why was I able to change the minimap by only using the latter?

Explanation/rant over.

I'm sure I've just overlooked something small. But, I'm unable to find it on my own, unfortunately.
Please tell me, if you know: How do I change the location of the scoreboard in Better BUG AI?


Thanks in advance to the person/persons that guides my way to the solution.
 
I believe the position of the BUG scoreboard is determined by the draw function in Python\BUG\Scoreboard.py. That is, unless the tabular layout is disabled on the BUG menu:
Code:
if (bAlignIcons):
    scores = Scoreboard.Scoreboard()
...
if (bAlignIcons):
    scores.draw(screen)
(from CvMainInterface.py)

I had never noticed the CvMainInterface-PLE.py. It's not present in BUG/K-Mod, only in BULL-based mods. Perhaps someone else knows how it interacts with CvMainInterface.py.

Any type of file can be attached inside a zip archive. I've also been meaning to enlarge the minimap on the main interface (though not as much as in your screenshot), so I'd be interested in taking a look at your code at some point. (Some later point I mean; unless you keep getting stuck. But it looks like you're making good progress on this project.)
 
Oh! Thank you!
I was looking in the wrong file, then.
No wonder I was getting frustrated.

Here is the proof it worked!
Scoreboard.py.jpg


I've attached the two files as zip-files. [Python\Screens\CvMainInterface] and [Python\BUG\Scoreboard].
All of my changes are marked with #kjotleik, so a search should be easy.

Thanks again. I would probably have spent way too long on this on my own.
I have not tested this in a full game, yet. So things may happen. But at turn zero, it looks good.
 

Attachments

One tiny bit of info, if you please...

Mod: Better BUG AI
File: [Python\Screens\CvMainInterface.py]

I am looking for the name of the log-entries that are shown in the middle of the screen towards the top in-between turns. The log that shows "Autosaving...," "City X has grown to size 2," "Leader Y converts to Buddhism!" and more of these lines during gameplay.

What I want is to make the lines longer (and move them slightly to the left), and to have more of them on the screen at the same time.

I've searched the CvMainInterface.py file for "screen.show" but I've not found anything that could be used to screen.show( "Log-At-Top-Of-Screen-In-Between-Turns").

So: What do I need to search for in order to find it? And what is it called?

Of course, if this TOO is in another file, I'll be tearing my hair out (figuratively).
 
Of course, if this TOO is in another file, I'll be tearing my hair out (figuratively).
:lol:
If that is an other python file that's not that bad.
...as compared to being controlled by the exe :rolleyes:
 
I am looking for the name of the log-entries that are shown in the middle of the screen towards the top in-between turns. [...] What I want is to [...] have more of them on the screen at the same time.
That last part should be possible by decreasing(?) EVENT_MESSAGE_STAGGER_TIME in GlobalDefines.xml. As for names, the function in the EXE that displays those messages is named simply "addMessage".
 
As for names, the function in the EXE... that displays those messages is named simply "addMessage".

Thanks, f1rpo.

I failed to include the information that I've already decreased EVENT_MESSAGE_STAGGER_TIME, EVENT_MESSAGE_TIME and EVENT_MESSAGE_TIME_LONG from their previous values down to one (1).

If the names in EXE are only called "addMessage," does that mean they are not exposed to Python? And that I cannot change the location on-screen of those messages?
In that case, it is good that I at least know about it, so I don't waste more of my time trying to find it.

I can live without changing it further. It just would've looked a bit better, in my opinion.

:)
 
I believe they are exposed via CyInterface().addMessage(...). That said, issuing messages is unrelated to where they are displayed. I guess it should be a specific widget type that displays those messages, but I don't know which it is.
 
I believe they are exposed via CyInterface().addMessage(...). That said, issuing messages is unrelated to where they are displayed. I guess it should be a specific widget type that displays those messages, but I don't know which it is.

Thanks, Leoreth.

I'll take a look (search) through some py-files (starting with CvMainInterface.py, of course) this weekend. I'll report back IF I find anything, and succeeds in changing the location. Will search for both the "addMessage" string and any widget, to see if something comes up.
 
I looked through CvMainInterface.py and unfortunately nothing stood out as related to the message box. This really drives home how bad it is to procedurally draw the whole interface in imperative Python code. I hope modern games don't do their interfaces that way.

There are some very general widgets that are like "interfacetopleft" and so on. Maybe you can play with their boundaries a little and see if it affects the position of the message box. It's an annoying process but it should help you track down which part of the code draws it. There has to be some code that draws it, but its position may be wired directly relative to other components. Here's hoping.
 
Is there any way to specify the order of civilizations on the "Play Now" screen (like in scenarios), or is that controlled by the exe?
 
Anyone with more Civ4 python experience able to tell me why this doesn't work?

Code:
         for iTerrain in xrange(gc.getNumTerrainInfos()):
             self.animalTerrainList.append([])
             for iUnit in xrange(gc.getNumUnitInfos()):
                 unitInfo = PyInfo.UnitInfo(iUnit)
                 if unitInfo.isAnimal():
                     if unitInfo.getTerrainNative(iTerrain):
                         self.animalTerrainList[iTerrain].append(unitInfo)
         for iFeature in xrange(gc.getNumFeatureInfos()):
             self.animalFeatureList.append([])
             for iUnit in xrange(gc.getNumUnitInfos()):
                 unitInfo = PyInfo.UnitInfo(iUnit)
                 if unitInfo.isAnimal():
                     if unitInfo.getFeatureNative(iFeature):
                         self.animalFeatureList[iFeature].append(unitInfo)

The idea is to save animals to lists depending on their native terrain, then loop through terrain and spawn animals according to which list they're in.

The spawning part works wonderfully, but when I run this bit, Civ4 says that there's no such thing as "getFeatureNative()" or "getTerrainNative()" even though both are in the API.

...any ideas?
 
Is there any way to specify the order of civilizations on the "Play Now" screen (like in scenarios), or is that controlled by the exe?

I'm pulling this out of my bum, but I think for non-scenarios it's the same order as the XML file. For scenarios it'd be the order they were included in the scenario when the person originally set up the game, which is the same order they're in the worldbuilder file. If you wanted to change the order just edit the worldbuilder file to swap them around.
 
Anyone with more Civ4 python experience able to tell me why this doesn't work?

Code:
         for iTerrain in xrange(gc.getNumTerrainInfos()):
             self.animalTerrainList.append([])
             for iUnit in xrange(gc.getNumUnitInfos()):
                 unitInfo = PyInfo.UnitInfo(iUnit)
                 if unitInfo.isAnimal():
                     if unitInfo.getTerrainNative(iTerrain):
                         self.animalTerrainList[iTerrain].append(unitInfo)
         for iFeature in xrange(gc.getNumFeatureInfos()):
             self.animalFeatureList.append([])
             for iUnit in xrange(gc.getNumUnitInfos()):
                 unitInfo = PyInfo.UnitInfo(iUnit)
                 if unitInfo.isAnimal():
                     if unitInfo.getFeatureNative(iFeature):
                         self.animalFeatureList[iFeature].append(unitInfo)

The idea is to save animals to lists depending on their native terrain, then loop through terrain and spawn animals according to which list they're in.

The spawning part works wonderfully, but when I run this bit, Civ4 says that there's no such thing as "getFeatureNative()" or "getTerrainNative()" even though both are in the API.

...any ideas?
I think it is because unitinfo is a CyUnit type not a CvUnitInfo type.
 
I'm pulling this out of my bum, but I think for non-scenarios it's the same order as the XML file.
Sadly that isn't the case, Egypt is the first civilization in my XML but Assyria is shown first. So I'm pretty sure they're listed in alphabetical order.
For scenarios it'd be the order they were included in the scenario when the person originally set up the game, which is the same order they're in the worldbuilder file. If you wanted to change the order just edit the worldbuilder file to swap them around.
Yes, the corresponding function is CvWBInterface.getPlayerData(). This does work when I have less than, or as many playable civs as MAX_CIV_PLAYERS, but I'm working on a "RFC engine" which reuses slots quite frequently (more precisely, every time a civ's starting date is reached). And the scenario civ selection screen will only show more than MAX_CIV_PLAYERS civilizations if I specify NONE as each and every civ type. In that case all slots (excluding the player's slot of course) are filled randomly, which is exactly what I want. But all playable civilizations are listed in alphabetical order, like on the "Play Now" screen!
So I guess I'll just place a number at the beginning of all civilization names as a workaround.
 
Last edited:
So I guess I'll just place a number at the beginning of all civilization names as a workaround.
And once the game has started, you could remove the leading number. It might also be possible to prepend numbers only while the EXE is sorting the list. In the debugger, I see that the EXE calls CvInfoBase::getDescription repeatedly for each civ as the civ selection screen (under Play Now) is loaded, so it looks like getDescription gets called for every comparison that occurs while sorting the list. But I'm not sure if getDescription is called one last time for each civ when sorting is over, i.e. when the descriptions are put on the screen. And even if so, identifying that last call would be difficult.
 
Back
Top Bottom