1. We have added a Gift Upgrades feature that allows you to gift an account upgrade to another member, just in time for the holiday season. You can see the gift option when going to the Account Upgrades screen, or on any user profile screen.
    Dismiss Notice

Fixing an event in CvRandomEventInterface.py

Discussion in 'Civ4 - SDK/Python' started by SmokeyTheBear, Feb 14, 2020.

  1. SmokeyTheBear

    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?
     
  2. f1rpo

    f1rpo plastics

    Joined:
    May 22, 2014
    Messages:
    561
    Location:
    Germany
    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)
     
  3. SmokeyTheBear

    SmokeyTheBear Chieftain

    Joined:
    Sep 7, 2008
    Messages:
    10
    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
     
  4. f1rpo

    f1rpo plastics

    Joined:
    May 22, 2014
    Messages:
    561
    Location:
    Germany
    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: Feb 15, 2020
  5. f1rpo

    f1rpo plastics

    Joined:
    May 22, 2014
    Messages:
    561
    Location:
    Germany
    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: Feb 15, 2020
  6. SmokeyTheBear

    SmokeyTheBear Chieftain

    Joined:
    Sep 7, 2008
    Messages:
    10
    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?

    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: Feb 15, 2020
  7. SmokeyTheBear

    SmokeyTheBear Chieftain

    Joined:
    Sep 7, 2008
    Messages:
    10
    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!
     
  8. f1rpo

    f1rpo plastics

    Joined:
    May 22, 2014
    Messages:
    561
    Location:
    Germany
    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.
    With parentheses after getCultureLevel. ;)
    Or:
    if city.getPopulation() > 1 and city.getCultureLevel() > 1:
    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.
     
  9. SmokeyTheBear

    SmokeyTheBear Chieftain

    Joined:
    Sep 7, 2008
    Messages:
    10

    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:

    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: Feb 15, 2020
  10. f1rpo

    f1rpo plastics

    Joined:
    May 22, 2014
    Messages:
    561
    Location:
    Germany
    :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:
     
  11. openyourmind

    openyourmind Chieftain

    Joined:
    Feb 22, 2016
    Messages:
    51
    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
     
  12. SmokeyTheBear

    SmokeyTheBear Chieftain

    Joined:
    Sep 7, 2008
    Messages:
    10
    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: Feb 19, 2020 at 1:33 AM
  13. f1rpo

    f1rpo plastics

    Joined:
    May 22, 2014
    Messages:
    561
    Location:
    Germany
    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.
     

    Attached Files:

    openyourmind likes this.
  14. SmokeyTheBear

    SmokeyTheBear Chieftain

    Joined:
    Sep 7, 2008
    Messages:
    10
    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.
     
    f1rpo likes this.

Share This Page