Quick Modding Questions Thread

@Set: Sounds like onEndGameTurn isn't getting called unless Python scripts get reloaded. You could verify that by writing something to PythonDbg.log at the start of onEndGameTurn (after setting LoggingEnabled to 1 in CivilizationIV.ini). E.g. just
print("onEndGameTurn called")
Are you working with a BUG-based mod? Maybe the BugEventManager is interfering.
 
How can I code this event, and where please?
Use some python in onBeginPlayerTurn
Check the game turn with gc.getGame().getGameTurn().
Set pCity to Damascus.
Code:
           iBuilding = gc.getInfoTypeForString("BUILDING_PALACE")
            pCity.setNumRealBuilding(iBuilding, 1)
 
@Set: Sounds like onEndGameTurn isn't getting called unless Python scripts get reloaded. You could verify that by writing something to PythonDbg.log at the start of onEndGameTurn (after setting LoggingEnabled to 1 in CivilizationIV.ini). E.g. just
print("onEndGameTurn called")
Are you working with a BUG-based mod? Maybe the BugEventManager is interfering.

I'm working off vanilla BTS, nothing BUG related.
Print statements don't show up in the log. I tried for onEndGameTurn and when that didn't work I also tried attaching print statements to onBeginGameTurn and onCombatResult. Neither of those show up in the log as well. I then tried the Cultural Victory trick with reloading the python modules and that doesn't work now either. Basically it seems like BTS isn't using my CVEventManager at all anymore which makes me think I'm missing something very obvious. I tried a fresh copy of CVEventManager with no code changes except print statements and those still aren't in the log. But BTS was clearly using my CVEventManager before! At least when python modules were reloaded. And I feel pretty sure I haven't screwed up anything so basic as the file structure.

Might take a step back from modding for a couple days and come back, backup my mod files, do a fresh install of BTS and see if things are still wonky.
 
You should look at CvEventManager.py It'll be your home base for python modding in Civ when you start out. When you open the file (use a program like notepad++) you'll see methods that looks like this:

Code:
def onBeginGameTurn(self, argsList):
        'Called at the beginning of the end of each turn'
        iGameTurn = argsList[0]
        CvTopCivs.CvTopCivs().turnChecker(iGameTurn)

All code you place under one of these methods will execute when the method is called. The methods are called more or less when you'd expect them to be ie. onBeginGameTurn above is called at the start of each turn (well, not actually but you can think of it that way). Other useful methods in CVEventManager are things like onCombatResult (called at the end of a combat), and onUnitBuilt (called after a unit is built). You can see that most of them are pretty self explanatory.

LPlate2's suggestion is you stick some code under onBeginPlayerTurn that places a palace in Damascus. You can use the Civ4 Python API to look for methods that might help you with this. You can probably use something like getGameTurn() along with, as LPlate2 mentioned, setNumRealBuilding().

EDIT: Here's my quick attempt at the code:

Code:
pPlot = CyMap().plot(x,y)
pDamascus = pPlot.getPlotCity()
###Finds the X and Y coordinates of Damascus and gives us a pointer variable that represents a city we can now work with, you should substitute these coordinates for x and y
iTurn = CyGame.getGameTurn()
##Finds the current game turn
iPalace = gc.getInfoTypeForString("BUILDING_PALACE")
##Create an integer variable to represent the palace. This checks the appropriate XML file and assigns the variable based on the Palace's location in that list
if (iTurn == 100):
    pDamascus.setNumRealBuilding(iPalace, 1)
##If it's the correct turn (replace 100 with the correct turn) place a palace in Damascus


Obviously this is pretty quick and dirty. You probably want to add checks as to whether Damascus is owned by Arabia, or if it's been razed, or if the capital was already moved. You might also include some text which is displayed or make it so the capital moves if Arabia doesn't control it on the set turn but captures/founds it later. It's also not too difficult to throw in checks for other things you might want. Maybe only move the palace if Islam is present in Damascus or if a Arabia controls at least three other cities in the Levant.
 
Last edited:
A couple of days and a clean install later here's an update on my weird python problems:

With no other changes to the CvEventManager I added a print statement to onEndGameTurn. It prints a single time in the log on game start and never again unless I reload the python modules in which case it prints again. My understanding of how this method works is that it's called at the end of every turn ie. There should be several instances of onEndGameTurn in the python log if I play several turns of the game.

I tried moving the print statement to onEndPlayerTurn. This works as you would imagine. On a duel map 3 instances of onEndPlayerTurn are printed in the log for each turn I've played. Then I tried attaching the cultural victory code to onEndPlayerTurn. The code is as below:

Code:
def onEndPlayerTurn(self, argsList):
    'Called at the end of a players turn'
    iGameTurn, iPlayer = argsList
    print("OnEndPlayerTurn is Called")
    iCultureVictoryReq = 1000
    for iLoopPlayer in range(gc.getMAX_CIV_PLAYERS()):
        pPlayer = gc.getPlayer(iLoopPlayer)
        iPlayerCulture = pPlayer.countTotalCulture()
        if (iPlayerCulture >= iCultureVictoryReq):
            CyGame().setWinner(pPlayer.getTeam(), gc.getInfoTypeForString("VICTORY_NEW_CULTURE"))
         
    if (gc.getGame().getElapsedGameTurns() == 1):
        if (gc.getPlayer(iPlayer).isHuman()):
            if (gc.getPlayer(iPlayer).canRevolution(0)):
                popupInfo = CyPopupInfo()              
                popupInfo.setButtonPopupType(ButtonPopupTypes.BUTTONPOPUP_CHANGECIVIC)
                popupInfo.addPopup(iPlayer)

"onEndPlayerTurn is Called" continues to print appropriately but the cultural victory still works as before. That is, not at all unless I reload the python modules at which point I immediately win the game if I meet the cultural victory threshold. I may have had python exceptions disabled before (oops:mischief:) but now they are enabled and as I win a bunch of python exceptions are thrown up. Giving this error log:

Code:
ERR: Call function onEvent failed. Can't find module CvEventInterface
ERR: Call function onEvent failed. Can't find module CvEventInterface
Traceback (most recent call last):
  File "<string>", line 1, in ?
  File "<string>", line 52, in load_module
  File "CvEventInterface", line 13, in ?
  File "<string>", line 52, in load_module
  File "CvEventManager", line 32, in ?
  File "CvEventManager", line 386, in CvEventManager
NameError
:
name 'iPlayer' is not defined

Failed to load python module CvEventInterface.
ERR: Call function onEvent failed. Can't find module CvEventInterface
ERR: Call function onEvent failed. Can't find module CvEventInterface

Which I can't make head or tails of. iPlayer is a variable in the vanilla CvEventManager! Nothing to do with me. I don't really know what direction to go with this now. I can make other parts of the CvEventManager work. I have code for capturing units at the end of combat which I've placed in on its own and that code works fine. I specifically seem to have an issue with this bit of code and with onEndGameTurn. Is this anything like an issue somebody else has had before?
 
Last edited:
I don't think this is the source of your errors, but are you sure this line
Code:
        if (gc.getGame().getElapsedGameTurns() == 1):
and the following ones are supposed to be inside your for loop? They certainly aren't in original BtS code.

Anyways, which is line 386 in your file?
 
I don't think this is the source of your errors, but are you sure this line
Code:
        if (gc.getGame().getElapsedGameTurns() == 1):
and the following ones are supposed to be inside your for loop? They certainly aren't in original BtS code.

Anyways, which is line 386 in your file?

Oops, this is just an artifact of uploading code to civfanatics. It's not contained within the for loop in my actual code. Fixed this now.

Line 386 in my file is the same as line 379 in base BTS.
Code:
CvAdvisorUtils.resetAdvisorNags()
Or to look at the whole section of code
Spoiler Code :
Code:
def onEndPlayerTurn(self, argsList):
        'Called at the end of a players turn'
        iGameTurn, iPlayer = argsList
        print("OnEndPlayerTurn")
        iCultureVictoryReq = 2000
        for iLoopPlayer in range(gc.getMAX_CIV_PLAYERS()):
            pPlayer = gc.getPlayer(iLoopPlayer)
            iPlayerCulture = pPlayer.countTotalCulture()
            if (iPlayerCulture >= iCultureVictoryReq):
                CyGame().setWinner(pPlayer.getTeam(), gc.getInfoTypeForString("VICTORY_NEW_CULTURE"))
        
        if (gc.getGame().getElapsedGameTurns() == 1):
            if (gc.getPlayer(iPlayer).isHuman()):
                if (gc.getPlayer(iPlayer).canRevolution(0)):
                    popupInfo = CyPopupInfo()
                    popupInfo.setButtonPopupType(ButtonPopupTypes.BUTTONPOPUP_CHANGECIVIC)
                    popupInfo.addPopup(iPlayer)
        ##This is line 385. The line directly below this comment is line 386
        CvAdvisorUtils.resetAdvisorNags()
        CvAdvisorUtils.endTurnFeats(iPlayer)
The only additions I've made to CvEventManager at all are, the print statement, the new variable (iCultureVictoryReq), and the for loop. Only 7 lines, none of them particularly complex. I'm can't figure out why that would break line 386.
 
Last edited:
With no other changes to the CvEventManager I added a print statement to onEndGameTurn. It prints a single time in the log on game start and never again unless I reload the python modules in which case it prints again.
And this happens even with a mod that makes no changes to any other files? I'm attaching a mod that has only this one file with that one extra line (and the auto-generated .ini file). I'm getting one line of log output with this each time that I end my turn, as expected.
 

Attachments

  • TestMod.zip
    9.6 KB · Views: 36
And this happens even with a mod that makes no changes to any other files? I'm attaching a mod that has only this one file with that one extra line (and the auto-generated .ini file). I'm getting one line of log output with this each time that I end my turn, as expected.
I've made lots of changes to the XML, but nothing else to python and I haven't touched the dll

Thank you for testing this out/sending the file along f1rpo! Definitely feel closer to a breakthrough then I did before. I tried out your TestMod and it works on my computer. Which you would think rules out a faulty installation. But then I tried replacing CvEventManager in my mod with an untouched CvEventManager from my vanilla civ bts folder and again only added a single print() line after onEndGameTurn. No dice. It only prints once, or when reloading modules. This also applies in reverse. Copying my base game CvEventManager file over the one in your TestMod and adding the print() line doesn't work properly either.

Seems like my base game CvEventManager file might be corrupted somehow. If it is, it's not obvious to me how. I am using Steam though, which can make clean installations hard to do (I did delete the files left over in steamapps\common manually) and I have messed around a fair bit with modding in the past.

I'll use WinMerge to investigate the differences between the files tomorrow and update this weekend. I've attached my vanilla bts CvEventManager file if anyone has an interest in looking at it themselves
 

Attachments

  • CvEventManager.rar
    8.5 KB · Views: 39
Hi,

Where in the code would I need to edit in order to adjust the information displayed for the combat information, as displayed in the the picture?
 

Attachments

  • CombatOdds.PNG
    CombatOdds.PNG
    35.2 KB · Views: 43
CvGameTextMgr::setCombatPlotHelp has the line, int iCombatOdds = getCombatOdds(pAttacker, pDefender);
Where is the code for the getCombatOdds calculation?
 
Last edited:
in CvGameCoreUtils.cpp on line 595 ? I never used it but seems to be there. Let us know how it goes.



// FUNCTION: getCombatOdds
// Calculates combat odds, given two units
// Returns value from 0-1000
// Written by DeepO
int getCombatOdds(CvUnit* pAttacker, CvUnit* pDefender)
 
Copying my base game CvEventManager file over the one in your TestMod and adding the print() line doesn't work properly either.
The two files we've uploaded have the same contents apart from the print call. Something in your file properties then? Access permissions (of the .py file and along its path edit: I suppose, since the .py file I sent works in the same folder, the folders' permissions can't really be an issue)? Though I don't see how that would explain the behavior you're observing. :confused:
 
Last edited:
After a busy weekend I hoped I would come back to this and my problem would turn out to be a phantom. But I double checked everything and no such luck :(

WinMerge agrees with f1rpo that the files are the same except for the print() statement. Only new piece of information to add is that when adding my cultural victory for loop onto the bottom of f1rpo's CvEventManager's OnEndGameTurn it doesn't work but the print statement continues to work as expected! Not really sure where to go from here. Obvious next step is to try it on a different computer but I don't have easy access to one. Might be able to use one in a couple weeks. Might take a step back from this particular problem. In the meantime, I did get started on my WBS to BMP Converter while I was frustrated with this so definitely not complete loss!
 
Last edited:
in CvGameCoreUtils.cpp on line 595 ? I never used it but seems to be there. Let us know how it goes.



// FUNCTION: getCombatOdds
// Calculates combat odds, given two units
// Returns value from 0-1000
// Written by DeepO
int getCombatOdds(CvUnit* pAttacker, CvUnit* pDefender)

Given that CvGameCoreUtils.cpp wasn't edited and compiled into the FFH DLL, I don't think I'll need to do any work on it.
I achieved what I wanted to achieve by switching to using combat percentage modifiers, rather than trying to do integer combat value boosts when certain terrain conditions are fulfilled.
(To achieve my original goal of an integer increase to combat values, I think I would approach it with autoacquistion and loss of promotions, which have an <iExtraCombatStr> value when the unit is on tiles that meet certain conditions. For the time being I'm not planning on doing autoacquisition and loss of promos.)
 
Anyone know how to control the number of hits/shots a unit takes during the combat animation?

Been playing Ashes of Erebus and my archers are pretty horrible. I counted one archer take 20 shots at a skeleton before pulling out a knife to finish the bugger off.
 
How is the description of the Pillage event managed ?

Context :
I created a Unit that can only do the action pillage road in own land (because you cannot pillage your own road in the base game).
Created boolean "bPillageOwn" in UnitInfo etc.
Edited ::canpillage, and ::pillage in CvUnit.cpp. All very straightforward, added a condition at beginning of the functions handeling this.

--> It's work fine, it pillages the road, independently of the tile having an improvement.

The description however says it will destroy the improvement first over describing the destruction of the road, simply because normally a normal unit destroy that first.
Where and how is the description handled ? It's not in CvGameTextMgr like it usually is. Must be a mission, event, or widget thing. Not sure where to look.
I don't see "TXT_KEY_MISSION_PILLAGE_HELP" called anywhere.

 
Top Bottom