Quick Modding Questions Thread

Looking at the transparent terrain, I don't think its really viable. Textures from adjacent tiles overlap, so I don't see how I could achieve a centrally placed transparent gap within a tile. Four touching tiles with a transparent blend can create a tile sized transparent area but its centered on where the grid lines cross.
Your suggested trompe l'oeil approach seems to be the only way to go.
 

Attachments

  • Transparent.PNG
    Transparent.PNG
    706.2 KB · Views: 38
Hey team, do you guys know where the code to place the turntimer on top-right of the screen is ?
I would like to move it a bit because it clashes with my interface on some resolution (NOT the one below)
upload_2021-10-20_22-25-13.png


Clearly, the text itself is generated in DLL in - void CvGameTextMgr::getTurnTimerText(CvWString& strText)

But I see no instance of "getTurnTimerText" being called anywhere. Not in DLL. And it's not even declared to Pyhon so it's not in CvMainInterface as I would have expected.
 
Hello! I'm getting tired of the AI's silly settling logic, so I thought I'd create an Earth map, fill it with city ruins, and forbid Settlers from settling anywhere except on top of city ruins.

So I went into 'PythonCallbackDefines.xml' and set 'USE_CANNOT_FOUND_CITY_CALLBACK' to '1'.

And then I went into 'CvGameUtils.py' where I changed function 'cannotFoundCity' to:

Code:
    def cannotFoundCity(self,argsList):
        iPlayer, iPlotX, iPlotY = argsList
        pPlayer = gc.getPlayer(iPlayer)
        pPlot = gc.getMap().plot(iPlotX, iPlotY)
        for i in range(0,pPlot.getNumUnits()):
            pUnit = pPlot.getUnit(i)
            if pUnit.getUnitType() == gc.getInfoTypeForString('UNIT_SETTLER'):
                if pPlot.getImprovementType() != gc.getInfoTypeForString('IMPROVEMENT_CITY_RUINS'):
                    return True
        return False

Why does this not work? I have tried multiple variants on this code, I have checked that this function is not called anywhere else - I have done everything I can think of, but no. I am using the mod RevolutionDCM, but I do not see any indication that this mod completely sidesteps this logic. Does anyone have any idea?

Thank you in advance!
 
Code:
    def cannotFoundCity(self,argsList):
        iPlayer, iPlotX, iPlotY = argsList
        pPlayer = gc.getPlayer(iPlayer)
        pPlot = gc.getMap().plot(iPlotX, iPlotY)
        for i in range(0,pPlot.getNumUnits()):
            pUnit = pPlot.getUnit(i)
            if pUnit.getUnitType() == gc.getInfoTypeForString('UNIT_SETTLER'):
                if pPlot.getImprovementType() != gc.getInfoTypeForString('IMPROVEMENT_CITY_RUINS'):
                    return True
        return False
![/QUOTE]
Without testing and just like that : I wonder if you need that clever about your loop selection.
Why can't you just have the "return true" part if it's not on a city ruins, i.e. :

Code:
if pPlot.getImprovementType() != gc.getInfoTypeForString('IMPROVEMENT_CITY_RUINS'):
         return True

You're only excluding some cases when it's already possible to settle, i.e. already a settler etc.
 
Without testing and just like that : I wonder if you need that clever about your loop selection.
Why can't you just have the "return true" part if it's not on a city ruins, i.e. :

CODE

You're only excluding some cases when it's already possible to settle, i.e. already a settler etc.
Thank you for your reply!

It's a clever notion, but unfortunately, the below also doesn't work:
Code:
    def cannotFoundCity(self,argsList):
        iPlayer, iPlotX, iPlotY = argsList
        pPlayer = gc.getPlayer(iPlayer)
        pPlot = gc.getMap().plot(iPlotX, iPlotY)
        for i in range(0,pPlot.getNumUnits()):
            if pPlot.getImprovementType() != gc.getInfoTypeForString('IMPROVEMENT_CITY_RUINS'):
                return True
        return False
 
I am using the mod RevolutionDCM, but I do not see any indication that this mod completely sidesteps this logic.
I think all BUG-based mods sidestep CvGameUtils.py by setting
GameUtils = BugGameUtils.getDispatcher()
in EntryPoints\CvGameInterfaceFile.py. CvGameInterface.py (not modified by BUG) then sets
normalGameUtils = CvGameInterfaceFile.GameUtils
If you don't want to familiarize yourself with BugGameUtils.py (I haven't looked into it), you could copy CvGameInterface.py from BtS and try placing your code in the cannotFoundCity function there. If it still has no effect, inserting a print statement should help narrow down the problem.
 
I think all BUG-based mods sidestep CvGameUtils.py by setting
GameUtils = BugGameUtils.getDispatcher()
in EntryPoints\CvGameInterfaceFile.py. CvGameInterface.py (not modified by BUG) then sets
normalGameUtils = CvGameInterfaceFile.GameUtils
If you don't want to familiarize yourself with BugGameUtils.py (I haven't looked into it), you could copy CvGameInterface.py from BtS and try placing your code in the cannotFoundCity function there. If it still has no effect, inserting a print statement should help narrow down the problem.
You are right! I see that callbacks are defined in 'BugGameUtils.py', and seemingly BUG just makes random .py files with a variety of related callbacks. So I went into UnitUtil.py and simply added my cannotFoundCity callback.

Now, if I leave 'USE_CANNOT_FOUND_CITY_CALLBACK' in 'PythonCallbackDefines.xml' at 1, the game refuses to load fully upon pressing play (to launch a random custom game), and I need to shut down my PC to get my PC to do something. If I set 'USE_CANNOT_FOUND_CITY_CALLBACK' to 0, the game will work, but Settlers can settle anywhere.

This seems strange, because shouldn't BUG be overriding this? And, if it apparently doesn't, why would my game crash during the loading?

Something interesting to note is that, if in the default game, you change all XML tags '<bFound>1</bFound>' in 'CIV4TerrainInfos.xml' to 0 (i.e. you can't find a city anywhere), the game also refuses to load and gets stuck in this infinite crash.

Note also that the logfile 'PythonDbg.log' gets stuck in an infinite loop (or something), as can be seen here, where it keeps doing a player/pass/failed thingy: https://i.imgur.com/pdAO0bO.png - I have attached the file (renamed to .txt).
 

Attachments

Note also that the logfile 'PythonDbg.log' gets stuck in an infinite loop (or something), as can be seen here, where it keeps doing a player/pass/failed thingy: [...]
I've done a text search for those strings, and, apparently, the debug messages originate from the findStartingPlot function in CvMapGeneratorUtil.py. Some map scripts use that function (e.g. Pangaea, Archipelago, Custom Continents), others don't. The loop never terminates if no valid starting plots exist. Perhaps safer to switch to windowed mode for a while so that you can easily reach the task manager to kill the Civ4BeyondSword process if it gets stuck.

I guess your Python code will have to check whether the map is your Earth scenario – and require city ruins only in that case. There's
CyMap().getMapScriptName()
and I think that'll also work for scenarios and includes the "CivBeyondSwordWBSave" extension in the returned name. Well, you could just print to the log to see what gets returned. On the DLL-side, the returned string is a std::wstring – don't know what data type that becomes in Python and how to properly check for string equality.
You are right! I see that callbacks are defined in 'BugGameUtils.py', and seemingly BUG just makes random .py files with a variety of related callbacks. So I went into UnitUtil.py and simply added my cannotFoundCity callback.
I suppose adding your callback to some existing BUG module should work. Seems to work ...
[...] This seems strange, because shouldn't BUG be overriding this?
The XML callback guards are checked by the DLL, e.g. here: CvPlayer.cpp#L4900
This happens before the call to CvGameInterface.py, i.e. before the BUG override.
 
I've done a text search for those strings, and, apparently, the debug messages originate from the findStartingPlot function in CvMapGeneratorUtil.py. Some map scripts use that function (e.g. Pangaea, Archipelago, Custom Continents), others don't. The loop never terminates if no valid starting plots exist.
Thus also explaining what happens if you change <bFound> to 0 everywhere.

There is more to this though; if I launch my custom Earth scenario without any ruins, I start being defeated, and I can choose to switch to another civilisation 'None' (probably implying that every civilisation dies an immediate death): https://i.imgur.com/8QprSsp.png - Because there is still a function looking at whether there are any viable locations to settle, I suppose? I wonder if I can find and remove that function.

Even if I add a dozen city ruins to my map (to normal land tiles, so a Settler should be able to reach them, 30 or so tiles apart) and only select two players, so that both players should have a valid starting location, I still get the same error. I attached a bunch of logs, as I am not exactly sure what is useful from here; PythonErr seems to throw a completely different error than what we are talking about here (something about assimilation?).

Any idea what I can do with this?

Thank you for your help though, it's very informative!
 

Attachments

The Python crash results from a division by zero (in otherwise unrelated code):
avgNumCities = iWorldCities/iPlayersAlive
In other words, yes, all players get killed. In a quick test with my own mod, CvPlayer::canFound (in the DLL) was neither called when I started the Earth18Civs scenario (civs start with settlers) nor with the EuropeAD1000 scenario (civs start with cities). I see that "Rhyes Earth 50 civs" does not place any cities and assigns no starting coordinates. To my knowledge, starting sites are then assigned by the DLL, which will make canFound checks. Still, strange that the Ruins aren't already in place at that point. Does your Ruins check work correctly in savegames (with a couple of Ruins placed through WorldBuilder)?
 
The Python crash results from a division by zero (in otherwise unrelated code):
avgNumCities = iWorldCities/iPlayersAlive
In other words, yes, all players get killed. In a quick test with my own mod, CvPlayer::canFound (in the DLL) was neither called when I started the Earth18Civs scenario (civs start with settlers) nor with the EuropeAD1000 scenario (civs start with cities). I see that "Rhyes Earth 50 civs" does not place any cities and assigns no starting coordinates. To my knowledge, starting sites are then assigned by the DLL, which will make canFound checks. Still, strange that the Ruins aren't already in place at that point. Does your Ruins check work correctly in savegames (with a couple of Ruins placed through WorldBuilder)?
I inserted a few StartingX / StartingY locations in my Earth map, which indeed allowed the game to start. But I am not sure if my function works, or if my function simply isn't called.

I cannot settle anywhere, which would indicate that my function is called? But I cannot settle on ruins either - which would indicate that my function is called, but doesn't work correctly. But it defaults to 'True' (read on), so maybe it is never called. I don't know how I can verify this; I do not see a reference to my function being called in any of the log files, but then, maybe the callbacks wouldn't appear there. I put print('I am doing something') at the beginning of my function, but I can't find this in any log file either.

The only thing I can find is:

PythonDbg.log:
Code:
18:57:53 DEBUG: BugUtil - binding CvGameUtils.CvGameUtils.cannotFoundCity to <CvGameUtils.CvGameUtils instance at 0x1A0585D0>
18:57:53 DEBUG: BugGameUtils - creating callback cannotFoundCity
...
18:57:53 DEBUG: BugGameUtils - cannotFoundCity - setting default to True

Any idea where I should find this, to check if the function is called? Because if it isn't called, then the default of 'True' is never changed, and so I can never settle anywhere - but why would that be, how can I make it be called, then? And if it is called, then the function apparently doesn't work - but what could be wrong with the code?
 
Your function (the original version) looks fine, and, come to think of it, really shouldn't cause problems during map initialization because no settler units exist at that point. So you're probably right that it still isn't getting called. I would expect print in any of the Civ 4 Python scripts to write to PythonDbg.log.

I've taken another look at the documentation in BugGameUtils.py ... ugh. Sounds like something like
<gameutils handler="cannotFoundCity"/>
or
<gameutils module="???" handler="cannotFoundCity"/>
needs to be added in some Config XML file.
I'd just go with this:
[...] you could copy CvGameInterface.py from BtS and try placing your code in the cannotFoundCity function there.
That is:
Code:
def cannotFoundCity(argsList):
   if gameUtils().cannotFoundCity(argsList):
      return True
   # (Your code ...)
Unless you want to add more Python code in the future or feel that you've almost figured it out ... Or maybe someone more familiar with BUG can help.
 
A quick message before I go to bed - I don't think I fully understand your idea of copying the CvGameInterface.py. Would I copy the entire file to a certain folder, or would I copy the content of the file to another file? And regardless, wouldn't it cause all kinds of problems, because BUG replaces it with its own code - or doesn't even read the file, perhaps?
 
RevDCM doesn't have its own CvGameInterface.py, so it'll get loaded from BtS. Just copying that file from the BtS /Assets/Python/EntryPoints folder to the same place in RevDCM shouldn't affect the behavior of the mod at all. The DLL directly calls the functions in CvGameInterface.py, the BUG handlers only come into play once gameUtils() is called, e.g.
Code:
def cannotFoundCity(argsList):
   return gameUtils().cannotFoundCity(argsList)
So, if you replace that line, BUG should never learn about those particular DLL-to-Python calls (originating in CvPlayer::canFound). That would be fine because RevDCM doesn't act on them anyway. The snippet I've proposed would still let BUG do it's thing (which should have no effect if you revert your change that has caused BUG to always return True) and afterwards run your code.

I've just run a test with my (outdated?) version of RevDCM. At least for map scripts, it seems to work correctly.
Spoiler Code :
Code:
def cannotFoundCity(argsList):
   #CvUtil.pyPrint( "CvGameInterface.cannotHandleAction" )
   if gameUtils().cannotFoundCity(argsList):
       return True
   iPlayer, iPlotX, iPlotY = argsList
   pPlayer = gc.getPlayer(iPlayer)
   pPlot = gc.getMap().plot(iPlotX, iPlotY)
   # Just trying to save some time
   if pPlot.getNumUnits() <= 0:
       return False
   if pPlot.getImprovementType() == gc.getInfoTypeForString('IMPROVEMENT_CITY_RUINS'):
       return False
   for i in range(0, pPlot.getNumUnits()):
       pUnit = pPlot.getUnit(i)
       if pUnit.getUnitType() == gc.getInfoTypeForString('UNIT_SETTLER'):
           return True
   return False
Didn't change anything else except PythonCallbackDefines.xml.

:think: The settler check may confuse the AI. It'll only realize that it can't found a city once the settler gets there. Should perhaps throw the settler check out and only check whether it's the proper scenario (CyMap().getMapScriptName()) and whether there's a Ruins improvement. Ruins placed by the scenario should be present by the time that the DLL assigns the starting locations.
 
I experimented a bit, and I'm probably doing something wrong, because it's still not working.

In RevolutionDCM\Assets\XML\PythonCallbackDefines.xml, I set USE_CANNOT_FOUND_CITY_CALLBACK to '1': https://i.imgur.com/xIlqY5z.png
In RevolutionDCM\Assets\Python\EntryPoints\CvGameInterface.py, I put the function cannotFoundCity: https://i.imgur.com/QzHGnGF.png
In RevolutionDCM\Assets\Python\BUG\BugGameUtils.py, the only mention of cannotFoundCity is setting the default to 'True': https://i.imgur.com/g9gXLTf.png
In RevolutionDCM\Assets\Python\CvGameUtils.py, I commented out the function cannotFoundCity: https://i.imgur.com/pNzA9aO.png - if I comment it in, or if I replace this with the function in CvGameInterface.py, the below error disappears, and my Settler can't settle anywhere, regardless of ruins.

Then, I get this: https://i.imgur.com/1onUX2W.png - see also the attached error log.
So I am not sure if it can actually sidestep the BUG callback system like this. But hopefully I am simply overlooking something.

Thank you for your continued help! And, yes, this is something I realised last night; I wonder if the AI is actually capable of finding a spot to settle on, once this change finally works. Maybe it'll all be for nothing and I'm better off creating a new terrain type where I can play around with bFound. Because the XML is read from the DLL and thus the AI should be able to act on that, unlike on a Python check. But we'll see!
 

Attachments

Last edited:
I don't think you should set the default to True in BugGameUtils.py; that's probably the problem. And since your Python code is now in CvGameInterface.py, there should be no need to modify CvGameUtils.py. RevDCM doesn't normally contain a copy of that file (directory listing at SourceForge), so, unless you've changed something else there, the file should arguably be removed again. Edit (now that I've noticed your edit): Not commenting anything out in CvGameUtils.py (or just removing it) should take care of the Python error.
Thank you for your continued help!
Sure. Hope you'll actually get it to work in the end. Was also interesting for me to learn that the game can freeze when no valid city sites exist. That's really an unnecessary hazard, and I've fixed that in my own mod - to future-proof it.
Maybe [...] I'm better off creating a new terrain type where I can play around with bFound. Because the XML is read from the DLL and thus the AI should be able to act on that, unlike on a Python check. But we'll see!
Would have to be a copy of every terrain type I guess. I expect that would work just through XML and the scenario file, but sounds like more work. The AI assigns a found-city value of 0 to tiles whose canFound check fails, so the Python code should get taken into account in that way; but the planned city sites get computed (each turn) before any settlers start moving, so the settler-at-plot check is probably going to hamper the AI to some extent.

@P&nny:
[...] But I see no instance of "getTurnTimerText" being called anywhere. Not in DLL. And it's not even declared to Pyhon so it's not in CvMainInterface as I would have expected.
My bet would be that the EXE places the text directly, presumably at coordinates computed based on the screen resolution.
 
It works now - but only if you leave the following in CvGameUtils.py:
Code:
    def cannotFoundCity(self,argsList):
        iPlayer, iPlotX, iPlotY = argsList
        return False

Unfortunately, the AI is indeed incapable of handling it; they found new cities if the ruins happen to coincide with the AI's preferred location, and otherwise the Settler and its escort just keep standing on an unsettleable tile, forever. Still, we learned things! :p

So my next idea would be to create a unique terrain type - why would I need to create six? I figure I can just create a terrain type that won't ever be generated, and manually put that on the spots where I want a city to be founded. If I can find some kind of urban-looking terrain tile, it will even fit with my purposes. The map might look a bit ugly, but once the cities are up, you won't notice it that much, I figure.

We'll see!
 
So my next idea would be to create a unique terrain type - why would I need to create six?
Have you tried removing the settler unit check? That is:
Code:
def cannotFoundCity(argsList):
   iPlayer, iPlotX, iPlotY = argsList
   return pPlot.getImprovementType() != gc.getInfoTypeForString('IMPROVEMENT_CITY_RUINS')
(Certainly not going to work if the scenario file doesn't have enough ruins that the civs can start on.)

You wouldn't need six terrain textures, but I don't suppose you'd want all tiles that allow a city to be founded to have the same yields. My intuition would've been to duplicate the TerrainInfo of Grassland, Plains, Tundra, Snow and Desert, changing only the <Type> value of the copies (so that those values remain unique). However, specific terrain types get referenced by a bunch of other XML files, e.g. CIV4UnitInfos.xml (native terrains of units) and probably also by some Python scripts, so a bunch of stuff would have to be changed to make the duplicate terrain types work exactly like the originals.
 
Have you tried removing the settler unit check? That is:
But isn't that the whole point? Without that, Settlers could found cities on other spots than on ruins - or am I misunderstanding you?

With regards to the terrain types - you're right, yes, one would lose out on e.g. strength modifiers dependent on terrain, improvements dependent on terrain types, et cetera. Not an optimal solution, certainly - but better than watching most AI's play the game with only one city.
 
Back
Top Bottom