Quick Modding Questions Thread

Thank you! Good to know. Decided to give warriors the UNITAI_PILLAGE mission to see if that gets the AI plotting faster. So far seems to have worked a little. There are a lot of other things that may be preventing the AI from plotting so early in the game though.

One of these days I'm going to have to do a deep dive and figure out how the AI works. Often feels like the biggest limitation on doing cool modding stuff is knowing how the AI will react.
 
One of these days I'm going to have to do a deep dive and figure out how the AI works. Often feels like the biggest limitation on doing cool modding stuff is knowing how the AI will react.
Please, shear with us your findings.
 
Thank you! Good to know. Decided to give warriors the UNITAI_PILLAGE mission to see if that gets the AI plotting faster. So far seems to have worked a little. There are a lot of other things that may be preventing the AI from plotting so early in the game though.
It could also be that the AI starts preparing for war early on, but then takes a long time to assemble a city attack stack due to other early builds (settlers, workers, minimal defenders, minimal explorers) being prioritized very highly. If your mod isn't based on BBAI, then perhaps you can tell through the "enough-on-our-hands" response whether the AI has a war plan; I don't think BtS reveals that anywhere in Debug mode. (BBAI shows war plans when holding Alt while hovering over the scoreboard.)
 
  • Like
Reactions: Set
I'm trying to spawn some units under specific conditions and having a lot of trouble. The general idea is that when a specific civ (the Norse) enters their first war of the game they'll spawn a big batch of free units. I've implemented this as an XML event because I wasn't sure how to keep track of the number of wars the Norse had fought using only python. I figured I could set the event to non-recurring in the XML and that way it would only occur once. So far none of this works : /

In CvEventManager I have this:
Spoiler :
Code:
def onChangeWar(self, argsList):
       'War Status Changes'
       bIsWar = argsList[0]
       iTeam = argsList[1]
       iRivalTeam = argsList[2]
    
       if (bIsWar):
           iNorseId = 15
           if ((iTeam == iNorseId) or (iRivalTeam == iNorseId) and (gc.getGame().getGameTurn() != 0)):
               iEvent = gc.getInfoTypeForString("EVENTTRIGGER_GREAT_HEATHEN_ARMY_1")
               iNorsePlayer = gc.getPlayer(iNorseId)
               iNorsePlayer.initTriggeredData(iEvent, True, -1, -1, -1, -1, -1, -1, -1, -1, -1)
(Norse team ID is 15)

and in CvRandomEventInterface I have this:
Spoiler :
Code:
def greatHeathenArmy_1(argslist):
   iEvent = argsList[0]
   kTriggeredData = argsList[1]
   pNorseplayer = gc.getPlayer(kTriggeredData.ePlayer)
   iUnitLongship = gc.getInfoTypeForString("UNIT_VIKING_LONGSHIP")
   iUnitBerserker = gc.getInfoTypeForString("UNIT_VIKING_BERSERKER")
   iUnitBatteringRam = gc.getInfoTypeForString("UNIT_BATTERING_RAM")
   iUnitHorseman = gc.getInfoTypeForString("UNIT_HORSEMAN")
   iUnitLongbowman = gc.getInfoTypeForString("UNIT_LONGBOWMAN")
 
   for x in range(10):
       pNorsePlayer.initUnit(iUnitLongship, 62, 40, UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_WEST)
   for x in range(15):
       pNorsePlayer.initUnit(iUnitBerserker, 62, 40, UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_WEST)
   for x in range(6):
       pNorsePlayer.initUnit(iUnitBatteringRam, 62, 40, UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_WEST)
   for x in range(6):
       pNorsePlayer.initUnit(iUnitHorseman, 62, 40, UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_WEST)
   for x in range(3):
       pNorsePlayer.initUnit(iUnitLongbowman, 62, 40, UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_WEST)

In EventTriggerInfos I have what you'd expect: <iPercentGamesActive> is 100, <iWeight>-1</iWeight> and everything else set to -1/0/NONE. <bRecurring> is set to 0.

In EventInfos everything is -1/0/NONE except I have <PythonCallback>greatHeathenArmy_1</PythonCallback>.

The event triggers properly, ie. when the Norse declare war on someone else I get the event pop-up. But it triggers every time the norse declare war rather then only the first time. I also get the following python exception when the event triggers:

"NameError: Global name argsList is not defined"

in CvRandomEventInterface. And then no units spawn.

I don't really know how argsLists work for python events. I've imitated firaxis as much as possible, pulling from the barbarian uprising event. Nor sure what's causing this exception.

Also not sure if I'm using the right method for this kind of effect. Looking at RFC for instance I can see that the conquerors event is run using scripts. But I don't know how scripts work and I can't find any information out there about how they work.
 
That will teach me to mod late at night :)

Fixed now (and a lower case p as well). The code still doesn't do what I want it too. Setting recurring to 0 doesn't seem to stop it from triggering multiple times. The work around for now is to make it a python only event and associate a building with it. The event places the building there and as long as the building remains the event can't trigger again. Thanks to the circumstances of the mod I don't think I have to worry about the city being razed or anything. But I do imagine there is a better way to do this!
 
Setting recurring to 0 doesn't seem to stop it from triggering multiple times
Calling initTriggeredData ignores this. It's only used when triggering events the normal way.

Do you use BUG? Then you can save a flag via BugData to remember that it already has been triggered. Here's some (untested) sample code:

Python:
table = BugData.getTable( "YourModID" )
if "greatHeathenArmyTriggered" not in table :
    # Do things
    table["greatHeathenArmyTriggered"] = True

Behild the scenes, BUG uses ScriptData to store that stuff.
 
  • Like
Reactions: Set
Hi, I’m trying to import some unit art styles. In some cases, it’s working perfectly. For some other units, the new unit artstyle only appears to be applied to one of the units in a group and the others keep the default unit art. Has anyone come across this before? What causes it and how do you fix it?
 
Hi, I’m trying to import some unit art styles. In some cases, it’s working perfectly. For some other units, the new unit artstyle only appears to be applied to one of the units in a group and the others keep the default unit art. Has anyone come across this before? What causes it and how do you fix it?
I'm troubleshooting this exact thing right now. I was thinking perhaps the order the units are listed may matter. Probably not, but no idea what it could be.
 
OK. Nevermind. I'm dumb. :blush:
Apparently, you can omit one of the <StyleUnit> tags, and it doesn't throw an error. Everything after than simply doesn't get used in the game. So, double check the tags to ensure they are correct.:scan:
 
I've removed duplicate <UnitMeshGroup> entries and this seems to have sorted the issue.

Edit: This is not a good fix. It messes up where a civ's artstyle uses more than one unitarttinfo for a unit.
 
Last edited:
After reinventing the wheel many times I'm trying to make use of PyHelpers but getting a crash on startup.
Code:
from CvPythonExtensions import *
import CvUtil
import CvScreensInterface
import CvDebugTools
import PyHelpers
import CvWBPopups
import Popup as PyPopup
import CvCameraControls
import CvTopCivs
import sys
import CvWorldBuilderScreen
import CvAdvisorUtils
import CvTechChooser

# constants
gc = CyGlobalContext()
PyPlayer = PyHelpers.PyPlayer
PyInfo = PyHelpers.PyInfo
cyMap = CyMap()
cyGame = CyGame()
pyGame = PyGame()
Seems like civ really doesnt like:
Code:
pyGame = PyGame()
When that line is gone the game starts fine. Though obviously code dependent on that variable does not work. I definitely don't feel too confident with any of my code but it does look like the code in Baldyr's tutorial.
 
I'm using Genghis Kai's GEM, and when I go into WB to place civ start locations, then save the file to play as a scenario, the civs are not where I saved them. Instead, all of the settlers and warriors are piled up on top of each other in some obscure location where most of them will never even get access to enough space to found their first city. This seems to be a problem related to placing more than 50 civs on the map, though I'm not entirely sure even of that much. I have previously had success with 49-Civ scenarios, but it's been so long that I don't really remember how I did it. Over 50, however, is something I haven't figured out. I am using a 100CivDLL that works just fine, so I don't think it's that (not that I know the first thing about coding). My guess is that the problem is related to the map/scenario.

Does anyone have any idea how I could get more than 50 civs into their proper starting locations in a Giant Earth Map WB/scenario/map file? It's frustrating to waste all the time it takes to put 75-100 civs down in their real starting locations in WB, only to have the starting locations all jumbled and reset when I open the scenario.

PS I'm playing the ROM:AND mod. Part 2, I think.
 
Last edited:
@scrabbarista: If you open the scenario file in a text editor, what does it say about the StartingX and StartingY coordinates? I think it should look something like this:
Code:
BeginPlayer
   LeaderType=LEADER_SALADIN
   CivType=CIVILIZATION_ARABIA
   Team=0
   PlayableCiv=1
   StartingX=69, StartingY=40
   [etc.]
May also want to verify that the number of players and teams in the scenario file matches the maximal numbers allowed by your DLL. (That said, if you've used the same DLL for saving the scenario as for loading it, then I don't see how there could be a mismatch.)
 
Does anyone have any idea how I could get more than 50 civs into their proper starting locations in a Giant Earth Map WB/scenario/map file?
All you need to do is this:

1. Start a Game with this Scenario
2. Open Worldbuilder and save it as new WorldbuilderSave

--> All Player should now have their locaions assigned in your new WorldbuilderSave.

But it will also have saved a lot more than that of course. e.g. the GameSettings you had chosen.
If you want to still be able to change them, open the Worldbuilder Save e.g. with Notepad++ and copy according settigs from the original Scenario (also a WorldbuilderSave).

----

Doing it like @f1rpo suggested is also possible, but it is a lot more effort than just creating a new WorldbuilderSave.
(And more prone to errors.)
 
All you need to do is this:

1. Start a Game with this Scenario
2. Open Worldbuilder and save it as new WorldbuilderSave

--> All Player should now have their locaions assigned in your new WorldbuilderSave.

But it will also have saved a lot more than that of course. e.g. the GameSettings you had chosen.
If you want to still be able to change them, open the Worldbuilder Save e.g. with Notepad++ and copy according settigs from the original Scenario (also a WorldbuilderSave).

----

Doing it like @f1rpo suggested is also possible, but it is a lot more effort than just creating a new WorldbuilderSave.
(And more prone to errors.)

This misses the point. If I start the scenario with over 50 civs, then many of them end up starting literally on the same tile. I don't want that, so I wouldn't save that. Instead, I manually placed them all where I wanted them, and saved it. Unfortunately, they were still on top of each other when I reopened my saved work to play it as a scenario.
 
@scrabbarista: If you open the scenario file in a text editor, what does it say about the StartingX and StartingY coordinates? I think it should look something like this:
Code:
BeginPlayer
   LeaderType=LEADER_SALADIN
   CivType=CIVILIZATION_ARABIA
   Team=0
   PlayableCiv=1
   StartingX=69, StartingY=40
   [etc.]
May also want to verify that the number of players and teams in the scenario file matches the maximal numbers allowed by your DLL. (That said, if you've used the same DLL for saving the scenario as for loading it, then I don't see how there could be a mismatch.)

IIRC, I'd checked, and they had been moved from where I put them to being on the same XY spots.

I saw in another thread that a guy wanted to do the same thing and said he started at 100 and worked his way down to see how many civs the map could handle without this bug. IIRC, he got to 70.

EDIT: That was a planet generator, not the GEM.

EDIT2: I'm also not sure that was RoM.

Anyway, I've drifted into making yet another 50 civ scenario and am having a ton of fun playing as Indonesia right now.

I have a question about that that I'll post right under this. Thank you for your reply! If/When I get back around to this scenario idea, I'll be sure to pay close attention to XY's in the xml. I don't think it's the dll, because I always use the same one.
 
Last edited:
I have a question about modding a RoM AND2 scenario.

I've given each of the religions tech and holy cities, etc., to their appropriate civs. My scenario starts in 1367, so what ends up happening is that each civ founds its religion on the first turn. This is all well and good as far as playability, but it's slightly annoying that the religions screen says "founded 1367" for every religion. It looks cheesy.

Is there a way I could edit this screen to give my preferred founding dates for each religion?
 
And while I'm here... I've used a handful of "replacement civs" to represent the actual ones I wanted when they weren't available.

I use Morocco as Algeria, Mutapa as Tanzania, and two Chinas (Taiwan) and two Koreas. I used Arabia as Saudi Arabia. I used Ottomans for Turkey.

I think the other 43 civs I wanted were already in the megapack.

To bring this scenario to the next level, how can I get these 7 civs into it? I'm open to putting in entirely new civs (not creating them myself, though) or else just changing the names/titles and maybe the flags. (I've seen some flag mod threads already.) It would be enough for me if just the names and titles were different, tbh. I'm less concerned about UU's, UB's etc. It'd just be so much cooler if it said "Tanzania" instead of "Mutapa," that's all.

I downloaded Taiwan before, but wasn't sure how to make it playable with the other 120 or so civs in the megapack + BTS.

PS The theory behind the scenario is that the balance of power is commensurate with the real world in 2021, but overall development is more like it was in 1300 - 1400. I think only the US has Rennaissance tech, and many civs are ancient and classical. China is massive and powerful. The US is great, too. My point is, "realism" is not the absolute standard for this scenario: Roosevelt is leading America in 1367 when the game opens. That said, I still want it to be as immersive as possible to the fantasy/alternate history I've imagined.
 
Last edited:
I have a question about modding a RoM AND2 scenario.

I've given each of the religions tech and holy cities, etc., to their appropriate civs. My scenario starts in 1367, so what ends up happening is that each civ founds its religion on the first turn. This is all well and good as far as playability, but it's slightly annoying that the religions screen says "founded 1367" for every religion. It looks cheesy.

Is there a way I could edit this screen to give my preferred founding dates for each religion?
Seeing that the turn-founded stored by the DLL can't be modified through Python, hardcoding the dates in Assets\Python\Screens\CvReligionScreen.py may indeed be your best bet. I think this is the loop you'd have to modify:
Code:
xLoop = self.X_RELIGION_START
for iRel in self.RELIGIONS:
    if (gc.getGame().getReligionGameTurnFounded(iRel) >= 0):
        szFounded = CyGameTextMgr().getTimeStr(gc.getGame().getReligionGameTurnFounded(iRel), False)
        screen.setLabelAt("", szArea, szFounded, CvUtil.FONT_CENTER_JUSTIFY,
               xLoop, self.Y_FOUNDED, self.DZ, FontTypes.SMALL_FONT, WidgetTypes.WIDGET_GENERAL, -1, -1)
   xLoop += self.DX_RELIGION
Not all that easy to implement. The self.RELIGIONS list (which doesn't exist in Vanilla Civ 4, it's an AND thing) may contain the ids of all religions or only of those already founded, depending on an option on the BUG menu. The ids correspond to the order of XML\GameInfo\Civ4ReligionInfos.xml, starting at 0. getTimeStr expects a turn number, but I guess it would be more convenient to provide a year number. Unfortunately, no utility function for converting past years to turn numbers seems to exist. You could simply enter year strings ("AD 50" or so), but then you don't have translations. If you do use game turn numbers, you'll get different years for different game speed settings.
 
Top Bottom