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

Introducing: PyScenario - easy-to-use scenario event scripting for RFC

Discussion in 'Rhye's and Fall Modmods' started by Baldyr, May 16, 2010.

  1. LuKo

    LuKo The Royal Guard

    Joined:
    Aug 28, 2006
    Messages:
    1,501
    Location:
    Poland
    migrate(iPop=1,tCoords=None,eNationality=None,bRaze=False)
    How many, where, which nationality, do raze or leave at pop 1 when it would leave city with pop0 or less.

    BTW: Is it possible to make check considering 2 or more civs? (like "if Carthage and Rome is alive") And to make a event which fires another event (on the same turn and moment)?
     
  2. Baldyr

    Baldyr "Hit It"

    Joined:
    Dec 5, 2009
    Messages:
    5,530
    Location:
    Sweden
    Maybe have city name be an alternative way to define the target city? Or would it be sufficient to use the found() method?

    Should the eNationality argument also affect citizens leaving the city? What if you order 2 population points of Japanese citizens to exit a Chinese city, but there is none to be found? Would some other segment of the population take a hike, or would nothing happen? What if the portion of the population wasn't large enough to match the iPop value? (These things would probably never match perfectly.)

    What about this: All eNationality citizens are automatically evicted if the iPop value is negative and eNationality is set to anything but None? This means that if 50% of a Chinese size 10 city is Japanese, then even a setting of iPop=-1 would make 5 citizens leave.

    And what about refugees? Would the people turn up somewhere else, or perhaps be translated into Settler units? (One unit per two population points.)

    And what about having eReligion be a setting? Like a diaspora. :p

    The rule is one target player per Trigger, in order to keep the application manageable. But why not something like a alive(ePlayer) Condition just for checking if whether or not a Civ is alive? Then you would either be able to add several of these - or the method would accept any number of arguments. Like:
    Code:
    Trigger().alive(0,2,5,6)...
    Sounds good? :D

    Yes, this functionality was introduced recently and involves using the label() and fired() methods. First you label one Trigger with a unique name, and then you check whether or not a Trigger with that name has fired. Refer to section 3.10 in the documentation (Tutorial).

    At this point I'm sorta awaiting who is going to complete the first fully developed and fully tested PyScenario script. Because I was intending to use that as the official example (section 4 in the documentation). Right now I think LuKo is looking like the front-runner. :goodjob: (Look for his scenario in the Modmods forum!)
     
  3. Baldyr

    Baldyr "Hit It"

    Joined:
    Dec 5, 2009
    Messages:
    5,530
    Location:
    Sweden
    Ok, I've attached a new Custom module containing the functionality LuKo was requesting:

    alive ( <indexed values> )
    Check the previous post for usage. :king:

    To use this you have to install the Custom.py file and add this to your script:
    Code:
    from Custom import *
    Trigger.alive = alive
    See previous post about merging this Custom module with the one posted earlier. (They of course work separately. And both methods will be included in the next update.)

    Who cares to test this for me in a actual scenario? :goodjob:
     

    Attached Files:

  4. Baldyr

    Baldyr "Hit It"

    Joined:
    Dec 5, 2009
    Messages:
    5,530
    Location:
    Sweden
    Oh, and I've forgotten to tell about a hidden feature in the last update (v1.022, as it is known nowadays):

    On line #27 in the PyScenario.py file you can found this assignment statement:
    Code:
    bDisplayDebugMessages = False
    By changing this value to True there will be debug messages printed into the Python Debug Log. These might not make much sense to most users, but they would be available anyway.

    Note however that doing this will add lag to the game, so don't leave this feature enabled when playing! And if you distribute the application, please leave this value to its default setting!

    The things to look out for are these lines:
    Code:
    PY:Trigger initialized: <PyScenario.Trigger instance at 0x18C7E418>
    Because this is a readout of the actual Trigger object and its location in memory. So if you have, say, 10 Trigger constructors in your script and you see 10 of these lines in the log, that means that all of them got loaded into memory!

    Also, whenever the game is checking all the Triggers this will appear:
    Code:
    PY:PyScenario.checkTriggers(): 1, (0,)
    The first value is the total number of Triggers that are currently loaded. The second value changes with "game events" and there will sometimes be several values inside the parenthesis (its actually a tuple value).

    Furthermore, every single Trigger checked will be reported like this:
    Code:
    PY:
     PyScenario.checkTriggers() Test Trigger
    The Trigger label "Test Trigger" is showed in this example, but if none is supplied - then it would look like this:
    Code:
    PY:
     PyScenario.checkTriggers() <PyScenario.Trigger instance at 0x18C7E418>
    Note that the memory location ID is the same as on initialization!

    Also, this is an example of what is logged when Conditions are checked:
    Code:
    PY:Conditions.processConditions(): 1
    PY:Conditions.processConditions(): <Custom.AlivePlayers instance at 0x2F949468> (1,)
    The first line displays the number of Conditions in the Trigger. The second line is an example of an actual Condition instance.

    And the Actions:
    Code:
    PY:Actions.initActions(): 1
    PY:<PyScenario.Test instance at 0x18C7E648>
     
  5. Baldyr

    Baldyr "Hit It"

    Joined:
    Dec 5, 2009
    Messages:
    5,530
    Location:
    Sweden
    Ok, next up is a variant of the flip() Action for LuKo to test out. (Everyone is of course welcome to help out.) Download the attached Custom module from this post, install it and add this to your Scenario module:
    Code:
    from Custom import *
    Trigger.flip = flip
    I've added a new argument at the end:

    flip( name=None, eOwner=None, bMinorsOnly=True, bAIOnly=True, bSafe=True, bRaze=True)
    What it should do - or perhaps could do - is prevent to AI Civs from razing cities when they flip to them. My theory is that they don't only do this because they are at war with the other Civ, but also because its a conquest take over and not a trade.

    By setting bRaze=False the city acquisition will - at war time - be both a conquest and a trade - at the same time! :eek: If this doesn't do the trick, I could have "safe" flips always be traded ones and never conquests.

    I have only checked that the actual flipping works, and was hoping for some help with actual testing. This change won't make it into the next version unless someone can verify what exactly it does!
     

    Attached Files:

  6. LuKo

    LuKo The Royal Guard

    Joined:
    Aug 28, 2006
    Messages:
    1,501
    Location:
    Poland
    I think it's sufficient but adding a new options is always good.

    Yes, it should. If there is lees eNation culture, then it removes it completely.

    It could be an option but perhaps for other trigger (or if set iPop=None, eNationality=6, bAll=true)

    Don't need. We can always spawn settlers/add pop in related events.

    Thought it is going to fire at another turn (as when quest is fulfil you get the reward at the beginning of next turn).

    Thank you very much :)
     
  7. Baldyr

    Baldyr "Hit It"

    Joined:
    Dec 5, 2009
    Messages:
    5,530
    Location:
    Sweden
    It actually depends if the first Trigger fired during or in-between turns. But you should test things out anyway, so you'll be able to see how it works from case to case yourself.

    Good Luck! :goodjob:
     
  8. LuKo

    LuKo The Royal Guard

    Joined:
    Aug 28, 2006
    Messages:
    1,501
    Location:
    Poland
    Should update look like that:
    Code:
    from Custom import *
    Trigger.alive = alive
    Trigger.flip = flip
    ?

    And how do I spawn several units with details? units().AI().units().AI()?
    ...And how do I limit a message just to target civ?
     
  9. Baldyr

    Baldyr "Hit It"

    Joined:
    Dec 5, 2009
    Messages:
    5,530
    Location:
    Sweden
    Yes, assuming that you merged the code in both Custom modules. (Copy-paste everything from one to the other. You don't need to duplicate any import statements, though. But I don't think it would matter anyway.)
    Yeah, you've got the right idea. Those add-on methods stick to the one before (to the left of) them. So the code is actually looking up the previous unit spawning instance and adding the settings to it. (The AI() method itself isn't really a Action in itself, but is adding setting to the units() method.)
     
  10. Baldyr

    Baldyr "Hit It"

    Joined:
    Dec 5, 2009
    Messages:
    5,530
    Location:
    Sweden
    ...or you could just rename the Custom modules as, say, Custom_flip.py and Custom_alive.py - then you'd have to go:
    Code:
    from Custom_flip import *
    Trigger.flip = flip
    from Custom_alive import *
    Trigger.alive = alive
    Use whichever is more convenient for you. :p And with the next PyScenario update you will most likely be able to remove those lines again as the changes and additions they bring would have made their way into the PyScenario module already.
     
  11. LuKo

    LuKo The Royal Guard

    Joined:
    Aug 28, 2006
    Messages:
    1,501
    Location:
    Poland
    It would be better if there would be third trigger in check() (check( bHuman=False, bDead=False, iCivilization=None ) ) :)
     
  12. Baldyr

    Baldyr "Hit It"

    Joined:
    Dec 5, 2009
    Messages:
    5,530
    Location:
    Sweden
    No, the check() Condition is aimed at the target player - the Civ that you specify in the Trigger constructor. But you mean that it could be used to check another Civ also? Well... I'll have to think about that! :p
     
  13. LuKo

    LuKo The Royal Guard

    Joined:
    Aug 28, 2006
    Messages:
    1,501
    Location:
    Poland
    I started to use alive() condition but I have to vary CELTIC troops considering if ROME is human- or AI-controlled, so...
     
  14. Baldyr

    Baldyr "Hit It"

    Joined:
    Dec 5, 2009
    Messages:
    5,530
    Location:
    Sweden
    Ok, we'll try it then. :king: Its next on my agenda - the migration() method will have to wait.

    Another custom method coming up... Tomorrow. :D
     
  15. astrognash

    astrognash TXT_KEY_CUSTOM_USER_TITLE

    Joined:
    May 10, 2008
    Messages:
    204
    Gender:
    Male
    Location:
    The Empire of North Carolina
    I'd like to, if possible, have a check that checks if nation x is a vassal state to nation y.
    I want it, because I'd like to code to trigger the Arab renames for cities of Persia, Babylon, Egypt, and Carthage if they become vassals of Arabia, along with a couple other renames of the type.
     
  16. Baldyr

    Baldyr "Hit It"

    Joined:
    Dec 5, 2009
    Messages:
    5,530
    Location:
    Sweden
    We already have the vassalize() Action - perhaps a master() Condition then? It checks if the target player is the master of the eVassal Civ?

    Would that do it - or should it be the other way around? Would a vassal() or subject() Condition that checks if the target player is the vassal of eMaster be more useful?

    I would really like one method to take care of both situations. :p

    edit: What about an additional setting to the owned() Condition, by the way, that lets you count vassals tiles as belonging to the master? So you would add a bVassals=True setting and simply check if those city sites belong to eOwner=eArabia - the Condition would pass if the are owned either by eArabia or by any of its vassals.
     
  17. LuKo

    LuKo The Royal Guard

    Joined:
    Aug 28, 2006
    Messages:
    1,501
    Location:
    Poland
    Request for the first custom module:
    heads( iNum=1, eType=None, tCoords=None, bExist=True )
    So, if bExist=False it's met when country doesn't have that many units.
     
  18. Baldyr

    Baldyr "Hit It"

    Joined:
    Dec 5, 2009
    Messages:
    5,530
    Location:
    Sweden
    So the proposed setting would turn the Condition into its opposite, then?

    I think you can get this functionality even without it, though. First you create an off switch like:
    Code:
    Trigger().label("limit").heads(<arguments>)
    Then you create the Trigger you wanna use with the opposite heads() Check:
    Code:
    Trigger().fired("limit",bFired=False)...
    So the first Trigger only performs the heads() check and once it passes the "limit" labeled Trigger is set to fired. The second Trigger's fired() Condition will pass every time until the earlier Trigger fires. Then the Condition will never be met again and the Trigger won't fire any more.

    Is this sufficient or do you need the event to be able to fire again once the unit requirement is lower than the iNum value?
     
  19. Baldyr

    Baldyr "Hit It"

    Joined:
    Dec 5, 2009
    Messages:
    5,530
    Location:
    Sweden
    Attached is a Custom module containing variants of the following Condition methods:

    war( eRival=None, bWar=True )
    The arguments are the same, but there is now a pre-set value for eRival. With this None setting the Condition will fire if the target Civ is at war with any (alive major) Civ. (I used this option in the Great General modmod myself.)

    check( bHuman=False, bDead=False, ePlayer=None )
    The ePlayer argument is new and overrides the target player for the Trigger - but only for this Condition and not for the whole Trigger. Now it is possible to use the check() method against several Civs in one Trigger.

    owned( tCoords=None, eOwner=None, bVassals=False )
    The bVassals argument is new: If set to True it will also count tiles owned by vassals as belonging to the master.

    heads( iNum=1, eType=None, tCoords=None )
    The arguments are the same as before, but if you set the iNum value to None the method will no longer count the number of units that are required for the Condition to pass. The condition will instead not be met if there is any number of units of the requested configuration.

    Install the module and add this to your Scenario module:
    Code:
    from Custom import *
    Trigger.war = war
    Trigger.check = check
    Trigger.owned = owned
    Trigger.heads = heads
     

    Attached Files:

  20. LuKo

    LuKo The Royal Guard

    Joined:
    Aug 28, 2006
    Messages:
    1,501
    Location:
    Poland
    More and more requests. You must hate me :p
    I need sth like that: science(iBeakers=0, iTech=None) so you can add or subtract amount of beakers from target/now being researched technology.
    Another one: buildingyield(tCoords=None, iBuliding=0, iCommerce=0, iHammers=0, iFood=0, iGold=0, iScience=0, iCulture=0, iHappiness=0, iHealth=0) (target building in target city produces additional... (as in library event [library +2 science]). Really I need iFood but other this still may be useful).
    iHaH(iHealth=0, iHappiness=0, iDuration=-1, bFading=True) +/- Health/Happiness for civ, for iDuration turns (or infinitely if -1) lowered every 10 turns by 1 (like whipangry) or not.
     

Share This Page