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. Baldyr

    Baldyr "Hit It"

    Joined:
    Dec 5, 2009
    Messages:
    5,530
    Location:
    Sweden
    No... :lol: Its you who is helping me out.

    This seems doable, but can you exemplify what you're doing with it so that it will be useful to you?

    I don't think this is doable with Python - manipulation individual buildings in cities like this. But I can see what methods I can find in the CivIV Python API... (It does sound like a C++ mod to me.)

    You could probably have a yield on every turn but it wouldn't come from the building as such, but from the Trigger that clears a find-building type method every turn... :p This wouldn't be visible in the city screen either.

    iHaH? :confused: You'd like to change the amount of health and happiness in a city? I'll have to get back to you on this also...

    What exactly are you planning to do? Because this seems to me like modding that is beyond the scope of PyScenario (i e historical events and such).
     
  2. Bonci

    Bonci King

    Joined:
    Oct 22, 2005
    Messages:
    739
    Location:
    Tridentum - Italia
    I've noticed that when i reload my scenario i get a warning that says:"No valid scenario module found" even if the scenario worked perfectly until i reloaded and if i restart bts and the scenario it works...it's annoying because i have to restart bts every time i modify something :(
     
  3. Baldyr

    Baldyr "Hit It"

    Joined:
    Dec 5, 2009
    Messages:
    5,530
    Location:
    Sweden
    Do you in fact mean the "No Triggers loaded! Save Scenario module and reload." message?

    If anyone is getting this, its because the Scenario module can only be imported to the PyScenario module if all the Python files have been reloaded first. And this will only happen once you save a module - any module - like the Scenario module itself. You don't even have to make any changes, just save it.

    So, you can only reload the Triggers by pressing shift + alt + R once you've gotten the automatic "Reloading Python Modules..." message. Otherwise nothing happens - and you get the above warning.

    I know this is both complex and awkward, but I don't think I can "fix" it. :(

    But, if you really mean the "No valid Scenario module found!" message, then that indicates that there has been an exception but that I've hidden it for you, the user. :rolleyes: There will be a way of getting back those in the next update. :p

    And if the module loads alright - and is working - after you restart the game, then it could possibly be an issue with text encoding. A user recently discovered that he couldn't get things working unless he used UTF-8 encoding. Check the settings in your text editor!
     
  4. LuKo

    LuKo The Royal Guard

    Joined:
    Aug 28, 2006
    Messages:
    1,501
    Location:
    Poland
    Prize for conquering Greece- the land of knowledge.

    I thought that events are Python related and therefore it doable :) Worse, if I remember wrong and there is no such an event (but I doubt it) :p
    Anyway, it's prize for conquering Egypt- the granary of Rome (so if it would be possible to add yield to the city- very well :) ).

    Sorry, it should be HaH- HealthandHappiness was too long :p For the trigger I haven't got any important use. But when it's done I will find some place where it fits :p
     
  5. Baldyr

    Baldyr "Hit It"

    Joined:
    Dec 5, 2009
    Messages:
    5,530
    Location:
    Sweden
    Speaking of manipulating cities, look at this link and find all lines under the header CyCity that start with VOID. These are the methods that are available in the CivIV Python API for manipulating things - the other methods (INT, BOOL, STRING et c) are used to fetch information and can't change anything.

    It can be helpful to see what is actually available for requests... :p
     
  6. Baldyr

    Baldyr "Hit It"

    Joined:
    Dec 5, 2009
    Messages:
    5,530
    Location:
    Sweden
    Isn't granting a Tech with the discover() Action enough?

    Are you referring to the random events that were introduced in BTS? I believe these are a combination of C++, Python and XML, but I might be wrong. :rolleyes:

    It might be possible to increase the contents of a city's food box however, I'll just have take a look. It would still not be noted in the city screen though, but would rather "magically" appear in the food box in between turns.

    What about using the proposed migrate() Action for adding citizens instead? Because food equals citizens.

    Naming these things can be tricky - because you wanna restrict yourself to one single word. As said, I'll take a look and see if there is a way forward.
     
  7. LuKo

    LuKo The Royal Guard

    Joined:
    Aug 28, 2006
    Messages:
    1,501
    Location:
    Poland
    VOID setBuildingYieldChange (BuildingClassType eBuildingClass, YieldType eYield, INT iChange)
    void (int /*BuildingClassTypes*/ eBuildingClass, int /*YieldTypes*/ eYield, int iChange)
    or
    VOID setBaseYieldRate (YieldType eIndex, INT iNewValue)
    int (int /*YieldTypes*/, int iNewValue) - sets the base rate for YieldType
    doesn't do the work?
    and
    VOID setBuildingCommerceChange (BuildingClassType eBuildingClass, CommerceType eCommerce, INT iChange)
    void (int /*BuildingClassTypes*/ eBuildingClass, int /*CommerceTypes*/ eCommerce, int iChange)
     
  8. Baldyr

    Baldyr "Hit It"

    Joined:
    Dec 5, 2009
    Messages:
    5,530
    Location:
    Sweden
    Very good! I haven't had a change to delve into the API yet myself, so thank you for helping out!

    This is looking very promising and I'll have a closer look. :D
     
  9. astrognash

    astrognash TXT_KEY_CUSTOM_USER_TITLE

    Joined:
    May 10, 2008
    Messages:
    204
    Gender:
    Male
    Location:
    The Empire of North Carolina
    Another request. I need a method to check if a city exists on a certain tile. I'm trying to put in Pompeii becoming Neapolis in AD 79, and, in order that it starts over as a fresh city, with no buildings or population, I'm spawning a city on that tile, replacing Pompeii, but I need a way to stop it from firing if Pompeii was never built.
     
  10. Baldyr

    Baldyr "Hit It"

    Joined:
    Dec 5, 2009
    Messages:
    5,530
    Location:
    Sweden
    This sounds kinda complicated, but I guess that just how the cookie crumbles sometimes. :p

    Lets see... So its would be like a reversed valid() Condition, then?

    But what if we used...
    Code:
    Trigger(con.iRome).find("Pompeii").year(79)...
    ...instead? If the city exists on year AD 79, then the target tile for the Trigger will be the city's coordinates. (Whatever they are.) Then add the Action:
    Code:
    ...city(iX,iY,"Neapolis")
    But that still wouldn't prevent the city from spawning if Pompeii isn't present. So I guess we'll have to add something to this Trigger. :D

    If you look at the API entry for the name() method there is similar functionality there. The city can be set to only change name if the present name and coordinates match. But you don't wanna rename the city, you need to rebuild it. :rolleyes:

    So... perhaps a rebuild() method then? :crazyeye: It takes tCoords and name as potential settings, and builds a new city over the old one - if found. Hmm... I'll have to think more about this.

    Somehow I think that the find() method could still be used for this. Perhaps a Condition that looks for a valid target tile? So you could label the Trigger something like label("Pompeii found") and not have any actual Action. Then the Trigger would be set to fired.

    Then another Trigger could have a fired("Pompeii found",False) Condition that prevents it from firing if the first Trigger fired.

    All that is needed is a tile(iX,iY) Condition that looks for a target tile without setting one. The Condition is passed if there is a target tile, like defined by the find() method.

    I realize this may sound like a backwards way of doing it, but the tile() method (or whatever) would have to be useful for other situations also.

    Look at it like a add-on method to find() that turns it into a Condition. :goodjob: (Maybe it could be called key() in order not to cause confusing with tiles() and target()? Yeah, I think I have something to work with here. :goodjob:)
     
  11. Baldyr

    Baldyr "Hit It"

    Joined:
    Dec 5, 2009
    Messages:
    5,530
    Location:
    Sweden
    Ok, this was probably the easiest thing ever to do. :D

    Custom module attached, you know what to do. :p

    found( bFound=True )
    You put this after the find() method and you have yourself a Condition that looks whether or not any of the city names has actually been found. If bFound=False, then it works the opposite.

    Does this solve your problem?
     

    Attached Files:

  12. Bonci

    Bonci King

    Joined:
    Oct 22, 2005
    Messages:
    739
    Location:
    Tridentum - Italia
    lol it was that :goodjob:

    could you make a built() function that controls if the specified building has been built in the given city?

    oh and could you add to the year() function the same arguments of the turn()? or maybe only the probability one?

    thank you in advance ^_^
     
  13. Baldyr

    Baldyr "Hit It"

    Joined:
    Dec 5, 2009
    Messages:
    5,530
    Location:
    Sweden
    I'll have to do some testing and see what encoding is valid and add a troubleshooting section to the documentation. :rolleyes:

    Ok, I'll try to get to it during the coming week. I believe LuKo is already awaiting some stuff... :p

    Yeah, sure. Both must be valid for the game turn for the Trigger to fire, though.

    I'm guessing you wanna do have some random game years? :D
     
  14. LuKo

    LuKo The Royal Guard

    Joined:
    Aug 28, 2006
    Messages:
    1,501
    Location:
    Poland
    My requests are not a priority as I'm going for holidays :)
     
  15. Baldyr

    Baldyr "Hit It"

    Joined:
    Dec 5, 2009
    Messages:
    5,530
    Location:
    Sweden
    Aha! :goodjob: Have a nice time and see you in two weeks time, then! Hopefully there will be a new edition of PyScenario awaiting you and also a Custom module with all the stuff you requested (well, at least the stuff that is doable :p).

    So you have something to look forward to while boring yourself silly with not doing anything. :lol:

    Ps. I'll have a look at your latest script later. I'm actually exited. :king:
     
  16. Baldyr

    Baldyr "Hit It"

    Joined:
    Dec 5, 2009
    Messages:
    5,530
    Location:
    Sweden
    Ok, here it is then:

    built( eBuilding, name=None, tCoords=None )
    Any way of defining the city (by name and/or tile) will work - the actual name and tCoords arguments within the method itself are optional.

    Should the method be able to take several eBuilding values and check the city for their presence? (It would only pass if every single one is found.)

    What about plot lists? Or no target tile/area/list what-so-ever? Could the Condition check for several sites and pass if there is a city containing that building in any of the tiles? And if only a target player is defined within the Trigger - should it pass if any of the cities have this building? And if no target tile or target player exists - should the Condition pass if the building is available anywhere.

    The latter cases would probably involve checking if wonders have been built - or if they have been built in historical (or unhistorical) locations... :p
     

    Attached Files:

  17. Bonci

    Bonci King

    Joined:
    Oct 22, 2005
    Messages:
    739
    Location:
    Tridentum - Italia
    so fast :eek::goodjob:

    don't think it's necessary...we could just add multiple building conditions...but if you want...:mischief:

    yeah that could be useful :)
     
  18. Baldyr

    Baldyr "Hit It"

    Joined:
    Dec 5, 2009
    Messages:
    5,530
    Location:
    Sweden
    Yeah, but it wasn't a very tall order. Basically what the Condition does is run this line of code:
    Code:
    return pCity.isHasBuilding(self.building)
    The rest is just a very round-about way of figuring out what CyCity object the pCity variable refers to, while self.building is equal to the eBuilding value.

    According to the PyScenario design principle, the other arguments would have to go. This is the trade-off. Do we want the method to look like this:
    built( eBuilding, name=None, tCoords=None )

    ...or like this:
    built( <integer values> )

    Because then there would have to be a target() and/or find() method to pinpoint the actual city.

    Note that I didn't do all these secondary features because this is where it gets somewhat work intensive. It will also require some testing to get right... :rolleyes:

    Another question: Would it be even more helpful to be able to check how many buildings of the specified sort are present in cities belonging to a plot list or to a target player? (Or in the game in general.) Like how many Granary buildings are built by the Babylonians, or how many Harbor instances are present along the Mediterranean.

    Such an iNum argument would of course not do anything if only one city or tile is defined. So it kinda feels like another method altogether. :crazyeye:
     
  19. Baldyr

    Baldyr "Hit It"

    Joined:
    Dec 5, 2009
    Messages:
    5,530
    Location:
    Sweden
    This will be my agenda for this week:

    I'm working on a little scenario of my own and I want to update PyScenario before release. So now would be a good time to come in with any further requests as I need to start focusing on getting all the content posted here recently in and testing everything.

    Also, these Actions (that were requested by LuKo) are on my to-do list:
    • migrate(), for changing population and makeup of cities
    • yield(), for changing the base yield and the number of happiness/health in a city - permanently
    • output(), for changing the yields of a building type in a specific city
    Additionally, I think I'll add more features to the built() Condition, as discussed. We'll see how much of this I can manage to get ready for the update. I'm also sure I've missed some odd request recently so just ask again if you need something. :goodjob:

    It would also be helpful if all the stuff posted in Custom modules were properly tested before inclusion in the application. This is actually the reason for posting them pre-release.

    As a preview, I can promise an alternative way of spawning units with plot lists. And also a policy() Condition for checking Civics.
     
  20. Baldyr

    Baldyr "Hit It"

    Joined:
    Dec 5, 2009
    Messages:
    5,530
    Location:
    Sweden
    I'm currently squirming over how to implement some new rules for spawning units over map areas (i e with a plot list). I don't wanna change the rules for any script already made and I don't wanna complicate matters for scenario makers.

    I have come to realize, however, that it would be useful to be able to spawn units on random tiles inside foreign cultural borders. Namely rebel units. :D But the current rules don't allow for this to happen. :p

    So, I've experimented with changing the units() method to allow for something like this if both the iX and iY arguments are set - and there is a plot list defined. In short, this double target would first check if the target tile (single tile) is valid according to all the rules. If it isn't, then it will check if any of the tiles defined in the plot list are valid (the right domain, not in the mountains, not a city tile, et cetera) but will allow for cultural borders. And, if all fails, the spawn will revert to the original tile - defined in the units() method and force the spawn anyway.

    The problem with this is that it isn't very intuitive. :rolleyes: And its not very easy to document in a way that makes perfect sense.

    So, one possible solution could be to add yet another units spawning method, like a spawn() method. While units() is designed to be used for a single map tile - and garrison() is designed for cities - the spawn() method would be adapted to be used with plot lists. And the preset AI setting could be something like UNITAI_ATTACK.
    The API could look something like this:
    spawn( eType=None, iNum=1, bBorders=True )

    Note that there are no iX/iY or tCoords argument(s), as there would have to be a plot list defined elsewhere with target() or tiles(). (Although a single tile or a plot list only consisting of one tile are still valid.) The default bBorders boolean setting could allow for spawning within any cultural borders.

    Would you prefer one less method with overlapping usage, or should we really add another?

    Another possible approach could be to rethink the whole setup for spawning units. :rolleyes: This is still in beta-testing, after all.

    I suspect having two separate methods - units() and garrison() - is already confusing. Maybe replace both with a spawn() method with all the current settings - and some new ones? (All existing scripts would be backwards compatible, of course, as the old methods would still be in there. They would just be removed from the documentation as they aren't necessary any more.)

    What do you think? Maybe just add a rebels() method instead, that spawns Barbarian units - but only within the target player's borders? :D (There could be a setting for changing the owner though.) I'm at loss... :crazyeye:
     

Share This Page