Fixing an event in CvRandomEventInterface.py

SmokeyTheBear

Chieftain
Joined
Sep 7, 2008
Messages
10
The Partisans event in Civ4 is bugged. 3.19 BTS

It is not supposed to occur for size 1 cities but it does anyway.

In CvEventManager.py, I found the cause. The line "if city.getPopulation > 1" is missing brackets, it should be "if city.getPopulation() > 1".

It almost always spawns 4~5 units because it uses the city's Tile Culture Level to calculate the number of partisan units. The Tile Culture Level of the plot a city is on grows extremely fast/is extremely high due to game mechanics.
I believe it was meant to use the City Culture level instead.

In CvRandomEventInterface.py, this defines the number of units created.

def getNumPartisanUnits(plot, iPlayer):
for i in range(gc.getNumCultureLevelInfos()):
iI = gc.getNumCultureLevelInfos() - i - 1
if plot.getCulture(iPlayer) >= gc.getGame().getCultureThreshold(iI):
return iI
return 0

How do I change it so that it uses the city's Culture Level instead of plot Culture Level?
 
Good catch. I've moved that piece of code from CvEventManager.py to the DLL recently in my own mod; that must've fixed the error, but I hadn't even noticed that there was one, i.e. the missing parentheses. I agree that comparing plot culture with the culture level threshold might be another bug, or perhaps the programmer didn't even know how the culture system works.

plot.getPlotCity() should provide the CyCity object. I'm pretty sure that 'plot' is the city plot and not one of the plots where the partisans will appear. plot.getCityPlot().getCultureTimes100() should then yield the city culture value at times-100 precision. One could compare that with 100*gc.getGame().getCultureThreshold(iI). But I'd simply try (haven't tried it):
Code:
if plot.isCity():
[TAB]return plot.getPlotCity().getCultureLevel()
else:
# (The BtS code as a fallback)
 
Thanks for the help.

Unfortunately the code doesn't work, no partisans appear.

Did I put it correctly?
Code:
def getNumPartisanUnits(plot, iPlayer):
    if plot.isCity():
        return plot.getPlotCity().getCultureLevel()
    else:

    for i in range(gc.getNumCultureLevelInfos()):
        iI = gc.getNumCultureLevelInfos() - i - 1
        if plot.getCulture(iPlayer) >= gc.getGame().getCultureThreshold(iI):
            return iI
    return 0
 
I've run a test. It seems that culture levels start at 0, so it should probably be:
return plot.getPlotCity().getCultureLevel() + 1
I think that, when the population check is made, city population has already decreased by 1 from conquest, so the city will usually need 3 population before conquest in order to receive partisans.
And everything under "else:" needs to be indented by one more tab.
 
Last edited:
Cripes. getCultureLevel is based on the city culture of the new owner. One has to use the BtS loop after all. I'll edit this post in a few minutes when I've (hopefully) gotten it to work.

Edit (needs tabs instead of spaces though):
Code:
def getNumPartisanUnits(plot, iPlayer):
    for i in range(gc.getNumCultureLevelInfos()):
        iI = gc.getNumCultureLevelInfos() - i - 1
        # Note that CyCity.getCultureLevel can't be used b/c iPlayer no longer owns the city.
        if plot.isCity():
            iCulture = plot.getPlotCity().getCulture(iPlayer)
        else:
            iCulture = plot.getCulture(iPlayer)
        if iCulture >= gc.getGame().getCultureThreshold(iI):
            return iI
    return 0
 
Last edited:
I am testing it against a 5-population city, so I don't think the population is a problem. Size 1 cities cannot trigger the event with the "if city.getPopulation() > 1" fix.

I tried the code with the + 1 and only one partisan unit spawned in a city with 3365/5000 Culture (Refined, Level 4). I removed the + 1 and no partisans spawn. Somehow the code is always returning 0. Any idea?

Cripes. getCultureLevel is based on the city culture of the new owner.

Ohhhh. That makes a lot of sense. I control the city right before razing it, and it has no culture level.

What I mean to do is use the City's Culture level and -1 for the number of partisans. So the razed Refined city would spawn 4 -1 = 3 units
From http://civ4bug.sourceforge.net/PythonAPI/
0 = CULTURELEVEL_NONE
1 = CULTURELEVEL_POOR
2 = CULTURELEVEL_FLEDGLING
3 = CULTURELEVEL_DEVELOPING
4 = CULTURELEVEL_REFINED
5 = CULTURELEVEL_INFLUENTIAL
6 = CULTURELEVEL_LEGENDARY

That brings me to another question, I would also have to disable the event triggering for None or Poor Culture cities in CvEventManager.py, otherwise a Partisan event message would pop up but no partisan units would be created.
Would the code be
"if city.getCultureLevel > 1" ? Can I just put it next line after "if city.getPopulation() > 1" ?
 
Last edited:
Cripes. getCultureLevel is based on the city culture of the new owner. One has to use the BtS loop after all. I'll edit this post in a few minutes when I've (hopefully) gotten it to work.

Edit (needs tabs instead of spaces though):
Code:
def getNumPartisanUnits(plot, iPlayer):
    for i in range(gc.getNumCultureLevelInfos()):
        iI = gc.getNumCultureLevelInfos() - i - 1
        # Note that CyCity.getCultureLevel can't be used b/c iPlayer no longer owns the city.
        if plot.isCity():
            iCulture = plot.getPlotCity().getCulture(iPlayer)
        else:
            iCulture = plot.getCulture(iPlayer)
        if iCulture >= gc.getGame().getCultureThreshold(iI):
            return iI
    return 0

This totally works!
I added - 1 after "return il" and it works exactly as I want, and I think the original intent of the creator.

This has been bothering me for ages.
Thanks a lot!
 
Ohhhh. That makes a lot of sense. I control the city right before razing it, and it has no culture level.
I shouldn't have been so quick to assume that the Firaxis programmer somehow didn't think of the getCultureLevel function. Goes to show who really doesn't understand the culture system. :blush:
Those are indeed the entries in Civ4CultureLevelInfos.xml (Vanilla XML\GameInfo folder). But I'm not sure if getCultureLevel really returns 1 for a city with "poor" culture. I think it might return 0. And 1 for "fledgling" etc.
if city.getCultureLevel > 1" ? Can I just put it next line after "if city.getPopulation() > 1" ?
With parentheses after getCultureLevel. ;)
Or:
if city.getPopulation() > 1 and city.getCultureLevel() > 1:
This totally works!
I added - 1 after "return il" and it works exactly as I want, and I think the original intent of the creator.
Great. :thumbsup: Now that the 'for' loop makes sense to me, I agree that the original author must've meant to call getCulture on the city.
 
if city.getPopulation() > 1 and city.getCultureLevel() > 1:
I tested this, it didn't work (event never triggers), and almost immediately I realised that's because at that point the city is under my control!
Just like our initial "getCultureLevel" in def getNumPartisanUnits :lol:

Those are indeed the entries in Civ4CultureLevelInfos.xml (Vanilla XML\GameInfo folder). But I'm not sure if getCultureLevel really returns 1 for a city with "poor" culture. I think it might return 0. And 1 for "fledgling" etc.:

It works, however a Poor Culture city would pop-up the Partisan event message but create 1-1 = 0 partisans, hence I feel the Culture level check for the event trigger is necessary.
 
Last edited:
I tested this, it didn't work (event never triggers), and almost immediately I realised that's because at that point the city is under my control!
Just like our initial "getCultureLevel" in def getNumPartisanUnits :lol:
:shake:
So, under
if city.getPopulation > 1 and iOwner != -1 and iPlayer != -1:
... let's insert (indented):
Code:
iOwnerCultureLevel = 0
for i in range(gc.getNumCultureLevelInfos()):
    iI = gc.getNumCultureLevelInfos() - i - 1
    iCulture = city.getCulture(iOwner)
    if iCulture >= gc.getGame().getCultureThreshold(iI):
        iOwnerCultureLevel = iI
        break
if iOwnerCultureLevel > 1:
And then the rest (indented). :please:
 
Could you please post the corrected CvRandomEventInterface.py and CvEventManager.py for the not so friendly with python? I've done everything mentioned in thread but believe made a mistake somewhere since partisan event isn't firing for level 2 cities
 
:shake:
So, under
if city.getPopulation > 1 and iOwner != -1 and iPlayer != -1:
... let's insert (indented):
Code:
iOwnerCultureLevel = 0
for i in range(gc.getNumCultureLevelInfos()):
    iI = gc.getNumCultureLevelInfos() - i - 1
    iCulture = city.getCulture(iOwner)
    if iCulture >= gc.getGame().getCultureThreshold(iI):
        iOwnerCultureLevel = iI
        break
if iOwnerCultureLevel > 1:
And then the rest (indented). :please:

I have tested this, but it doesn't work, the event fails to trigger. I don't really understand the code though, so we'll have to wait for f1rpo to troubleshoot again!

The code for CvRandomEventInferface.py works perfectly though. So for now just change that, and add parenthesis () after "city.getPopulation" in CvEventManager.py and the fix works for the most part.
The only flaw is that Culture Level 1 (Poor) cities will trigger the event message but fail to spawn any Partisan units.
 
Last edited:
The attached scripts (containing, I think, just the code I had previously posted) work for me at least in the simplistic example shown in the attached screenshots. On turn 0, I've given Pericles' capital +2 population and +12 culture, to England a Tank and to Pericles a 2nd Settler and Democracy. Then took control of Pericles (Alt+Z), founded the 2nd city, switched to Emancipation, declared war on England. Then took control of England (Alt+Shift+Z) and razed Athens with the Tank.
 

Attachments

  • Partisans Py fix.zip
    Partisans Py fix.zip
    25.1 KB · Views: 142
  • before.jpg
    before.jpg
    115.2 KB · Views: 183
  • after.jpg
    after.jpg
    168.8 KB · Views: 165
Tested it and it works beautifully now!

I compared my code to f1rpo's CvEventManager.py, my indentation is wrong.

I think that's why openyourmind and I couldn't get it working.

Thank you once again, for your help.
 
Back
Top Bottom