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

Then you only use the first value (iYear1). Since the other value (iYear2) will be invalid (None) the setYear() method creates a one year interval. (Year2 = Year1).

The actual check is performed by this equation:
CyGame.getTurnYear(iGameTurn+1) > Year1 and CyGame.getGameTurnYear() <= Year2
This took me like ages to figure out, since I'm mathematically challenged. But it works with dynamically changing game year intervals - as on Normal speed. I can't see how it wouldn't work with any other year intervals also.
 
I think that Trigger( ePlayer, ... ) method is more convenient because player() would be used ~80% of times and label() ~10% :)
This is a valid point, but I never liked the label() method much...

It would however be a good way to document your script labeling all the Triggers. :king:
 
### method is better :P
Personally I lake label() method but I would never be able to understand what trigger does by the name of the label (some examples: REgypt, fall3a).

OK, "names" of messages are worse. But that's because I have more messages than labels :P

[EDIT]: "This took me like ages to figure out, since I'm mathematically challenged." LOL XD
BTW: GREAT avatar! You never had better :)
 
Then you only use the first value (iYear1). Since the other value (iYear2) will be invalid (None) the setYear() method creates a one year interval. (Year2 = Year1).

The actual check is performed by this equation:

This took me like ages to figure out, since I'm mathematically challenged. But it works with dynamically changing game year intervals - as on Normal speed. I can't see how it wouldn't work with any other year intervals also.

This works, though from what I understand
- you have to use ".once()" because without it year(a) may trigger many times
- the results will be different from those in the RFC code, meaning, you cannot reliably trigger something "on Roman launch", because getTurnForYear(a) may trigger on a different turn than year(a), since my function returns the closest year, and as far as I understood, yours effectively rounds it down?

suppose years go 220,230,240
- getTurnForYear(236) will fire on year 240
- year(236).once() will fire on year 230

Right? I originally also returned the first turn, effictively rounding it down, but it didn't match years in RFC on Normal speed, because the game also rounds it "fairly" (I'm not using the proper math terms here, but I hope you get what I mean, I'm mathematically challenged as well).

Well, at least it won't hurt to run a quick test comparing results year after year.
 
This works, though from what I understand
- you have to use ".once()" because without it year(a) may trigger many times
- the results will be different from those in the RFC code, meaning, you cannot reliably trigger something "on Roman launch", because getTurnForYear(a) may trigger on a different turn than year(a), since my function returns the closest year, and as far as I understood, yours effectively rounds it down?
Don't ask me how the algorithm works - it was some time ago I came up with it and it would only make my head hurt. :lol: I don't believe it has anything to do with rounding however.

The logic has, to my knowledge, worked perfectly in all respects. And RFC "Normal" has changing game year intervals also - so I can't see how it would be different with another set of intervals. :confused:

But the self.once = True is only used for reducing lag. Meaning that any Trigger that has both self.once == True and self.fired == True is never processed. So it could just as well be removed completely from PyScenario.triggers (list). This is all it is - a Trigger containing the year() method with only one parameter can only fire once, so it will be discarded after it has. It will make no difference what so ever for the issue at hand it the once() business is deleted.

Well, at least it won't hurt to run a quick test comparing results year after year.
Yeah, for sure! :king: Thanks for the help - I know you mean well. And you might well be right on this issue - I can only convince myself of anything math related by testing it myself. So we'll just see. The issue will be taken care of in any case.
 
CyGame.getTurnYear(iGameTurn+1) > Year1 and CyGame.getGameTurnYear() <= Year2

Do I understand it correctly:
"If year in another turn is higher than Year1 and year in this turn is not higher than Year2"
So depending how months are stored (as decimals?) it could work or not:
Year1=2000, Year2=None (2000)
2000,25>2000, 2000<=2000 ### fires once at 2000AD

Year1=2000, Year2=2001
2000,25>2000, 2000<=2001 ### fires at 2000 mar
2000,5>2000, 2000,25<=2001 ### fires at 2000 jun
2000,75>2000, 2000,5<=2001 ### fires at 2000 sep
2001>2000, 2000,75<=2001 ### fires at 2000 dec
2001,25>2000, 2001<=2001 ### fires at 2001 mar

Year1=1665, Year2=1670 (and turns for example goes: 1660, 1675, 1690)
1675>1665, 1660<=1670 ### fires once in 1670

So yeah, it rounds down the year but it works nice for any turn intervals unless months are not made as decimals.
 
Are there turn intervals shorter than one year in RFC Marathon? It never even occurred to me...
 
Am I right that heads() count units in a single tile and not in whole area? It doesn't seem right.

[EDIT]: After 2 days of testing I have to say that my stability modmod doesn't work as intended. Why? Because Trigger() doesn't take the first civ who meet the criteria but take all civs as a whole. So I have to make Trigger() for every civ :/ Is it possible to make a condition like random() which check criteria and take the first civ who meet them?
 
Am I right that heads() count units in a single tile and not in whole area? It doesn't seem right.
The idea is basically to lookup the number of units owner by the Target player, but the check can also be limited to one tile only. I guess it never occurred to me to check whole map areas, but that actually makes sense. I'll add this functionality when I get the chance.

What exactly are you trying to do with the heads() Conditions, by the way?

[EDIT]: After 2 days of testing I have to say that my stability modmod doesn't work as intended. Why? Because Trigger() doesn't take the first civ who meet the criteria but take all civs as a whole. So I have to make Trigger() for every civ :/ Is it possible to make a condition like random() which check criteria and take the first civ who meet them?
I'm not sure I understand exactly what you are trying to do, but whatever it is you should probably try doing it with some Python code in the Stability module instead. PyScenario isn't very good for actual "modding", nor was it ever intended for anything like this.

Take this as a opportunity to learn programming - or let me just make the stability mod-mod for you. (I guess I promised this once before? Well, I'll try to look into this time, ok?) In the latter case, could you send me the specifics in a PM? If you wanna learn Python, you can contact me with any questions you have also. Because I don't think you need to or should limit your modding to using PyScenario. (Its basically a set of limitations on how Python can be applied to the game anyway.)
 
Last 200 years or so on Marathon and last 42 years on Epic use 6 and 3 month increments.
I never dealt with fractions of years - how does that work with integer values for years? :confused:

Its good to know that you have a ready-to-use solution for this in any case...
 
What exactly are you trying to do with the heads() Conditions, by the way?

My trigger should fire if civ has 5 or more units. But since I have yields() in the same trigger I had to define target for that. And if I define even whole world as a target heads() check if there is a tile with 5 or more units on it.
Code:
Trigger(0).solidity(-30).operator(5).heads(5).stability(5).target((0,0),(123,67)).yields(0,0,-2,-1)

If you wanna learn Python, you can contact me with any questions you have also.

Any editor or sth like that? I downloaded one but didn't know how to make a code longer than 1 line (enter made the code run).
 
My trigger should fire if civ has 5 or more units. But since I have yields() in the same trigger I had to define target for that. And if I define even whole world as a target heads() check if there is a tile with 5 or more units on it.
Code:
Trigger(0).solidity(-30).operator(5).heads(5).stability(5).target((0,0),(123,67)).yields(0,0,-2,-1)
Outch! Having a plot list cover the whole map is not recommended use, but I can see what you were trying to do however.

A plot list doesn't however replace a regular target tile - they exist side by side. So if a method - like heads() - doesn't have support from plot lists it will just use the target plot coordinates instead. It won't even recognize the plot list, so the tCoords setting would be valid.

But in this case you are clearly looking up if the Target player has five or more units, so I'm not seeing how this wouldn't do what you want it to.

With that said, I'd still recommend hacking into the Stability module over trying to stretch the limits of PyScenario. It will only result in an inferior implementation.

Any editor or sth like that? I downloaded one but didn't know how to make a code longer than 1 line (enter made the code run).
When you download Python you get the default interpreter/editor called IDLE. It should open up two windows - one is the code editor and one is the command prompt. But you should probably use both if you wanna do programming. This textbook should get you started so that you don't miss any essential information from start.

Just PM me if you need more detailed instructions on how to get started.
 
I never dealt with fractions of years - how does that work with integer values for years? :confused:

Its good to know that you have a ready-to-use solution for this in any case...

There are no integer years because the default calendar uses months, so when you're asking for 600 AD you are really asking the game for month 7200. There are month-equivalent functions in the DLL but not exposed, RFCEM comes with this (exposed):

int getGameTurnForMonth(int iTurnMonth, int iStartYear, int eCalendar, int eSpeed)

which is really what getTurnForYear uses before conversion, but you can use it directly to ask for a specific month-turn, previously converting it in Python from some human-readable format like 2000.25 or something. Note that I never used it in RFCEM as it wasn't needed.
 
Ok, this is all new to me. The question now is how we wanna use the year() method with PyScenario?

Do we want a four turn year to also be a four turn interval? Because then you'd just have to add the once() method to turn it into a Condition that only fires on the first turn of that date.

Or do we really wanna use floating point values? I think this would only serve to make things confusing. On the other hand, I could make it possible to use whichever and just convert a integer argument to a floating point under the hood, so to speak. So the year method could still be used as before.

But on the other hand, there really should be some way of triggering events on a specific month - if this is applicable with regard to game speed.
 
If I indeed do move over PyScenario to a RFC:E/M setup, there would be this to consider also. It means, in short, that the game breaks down anytime you edit the Scenario module during game-play.

But I think it could be fixed, since you need to manually reload all Triggers once all the Python modules have reloaded anyway. So this short-cut could be augmented by having it also load the latest stored data. But this data would only be from the latest save, so enabling autosave on every turn is recommended. I do however believe that quick-save doest the job also. But note that you don't have to actually load the save game.
 
What about a month() Condition then? It would work just the same as year() but if the game year is made up of more than one game turn, then month() would pass only if the "leftover" is also valid. The game actually has MonthTypes.

I'm very unsure on how to implement something like this, though, but here is an example of use:
Code:
Trigger().year(1939).month(8)...
or
Code:
Trigger().year(1939).month("MONTH_SEPTEMBER")...
or
Code:
Trigger().year(1939).month(MonthTypes.MONTH_SEPTEMBER)...
It would probably be possible to add another optional argument to create a month interval:
Code:
Trigger().year(1939).month(7,9)...
So this Trigger would fire on any game turn within August of 1939 and Oktober of 1939. (Note that months are numbered from 0 to 11.)

The downside of such a setup would probably be that it wouldn't be possible to have a year/month interval ranging from say December of 1939 to January of 1940. This would involve having two Triggers where the second one looks up if the first one has fired.

Any other suggestions?
 
YAY Baldyr! Keep up the good work! I'm so happy the conversion for RFCE/M is underway!:)
goodwork!:goodjob:
 
There are no integer years because the default calendar uses months, so when you're asking for 600 AD you are really asking the game for month 7200. There are month-equivalent functions in the DLL but not exposed, RFCEM comes with this (exposed):

int getGameTurnForMonth(int iTurnMonth, int iStartYear, int eCalendar, int eSpeed)
So the getGameTurnForMonth() function returns iGameTurn then, right. What about the iTurnMonth argument? This is the "real" game date, then? (Like 7200 for January of AD 600, but this would vary depending on speed.) And I can just get iStartYear with CyGame.getStartYear(), eCalendar with CyGame.getCalendar() and eSpeed with CyGame.getGameSpeedType(). So I could shorten the function by putting it in my own wrapper function:
Code:
def getTurnForMonth(iMonth):
    return getGameTurnForMonth(iMonth, gc.getGame().getStartYear(), gc.getGame().getCalendar(), gc.getGame().getGameSpeedType())

which is really what getTurnForYear uses before conversion, but you can use it directly to ask for a specific month-turn, previously converting it in Python from some human-readable format like 2000.25 or something.
How do I get from a floating point date like 2000.25 to a integer game turn value? :confused: Could you perhaps give me an example?
 
So the getGameTurnForMonth() function returns iGameTurn then, right. What about the iTurnMonth argument? This is the "real" game date, then? (Like 7200 for January of AD 600, but this would vary depending on speed.) And I can just get iStartYear with CyGame.getStartYear(), eCalendar with CyGame.getCalendar() and eSpeed with CyGame.getGameSpeedType(). So I could shorten the function by putting it in my own wrapper function:

Exactly, getTurnForYear is a wrapper as well. iTurnMonth is month number, like month 7200, which is always 600 AD, it's the result (game turn) that changes depending on speed. It's just the naming they used in the SDK, i.e. iTurnYear is simply year.

How do I get from a floating point date like 2000.25 to a integer game turn value? :confused: Could you perhaps give me an example?

like this:

Code:
iGameTurn = getTurnForMonth(int(2000.25 * 12))

though since you're getting input from year(iYear) and month(iMonth) you'd be just using iYear * 12 + iMonth, not sure what's more simple really.
 
Back
Top Bottom