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

I'm trying to wrap my head around the math involved, but I'm sure you're right on the implementation. So the question that beckons is whether we want floating point values in the year() method or if we wanna use integers and add an optional month with the month() method?

It could be added that a year() method that takes floating point values (like 2000.25) can also be made to accept integer values (like 2000), so the fraction representing the month would still be optional.

Perhaps we could have years() also accept string representations of year/month, like year("2000:3") for the month of March of the year AD 2000. The application would simply parse the values inside the string and convert them into integer values for the actual processing. The advantage being that we would be able to have intervals that include months, like:
PHP:
Trigger().year("1939:12","1940:1")...
Which would trigger on any game turn between December of AD 1939 and January of AD 1940.

edit: Or we could just allow for all variants. More programming for me to figure out, goodie! :D
 
Progress report:

Right now the year() Condition can handle all these variant arguments, or any combination thereof:
PHP:
year(1939)
year(1939,1940)
year(1939.75)
year(1939.9,1940.1)
year("1939")
year("1939.75")
year("1939:9")
year("1939:12","1940:1")
The rules are that you can't use a an actual comma as a decimal comma, and if you wanna use a colon for specifying the month the entire value must be a string (inside a pair of quotations).

Clearly this is the most versatile approach, not?

Now I have to give this a through testing, which might take a while... :p
 
I would really like some input from anyone else actually using PyScenario on moving ePlayer (and eCivilization) from the Trigger constructor and adding a player() method. This method would only be used with Triggers than needs a target player. (The eCivilization argument could be reappear in a civilization() method for those who wanna use this instead.)

Also, the label() method would be retired and this setting would be done inside the Trigger constructor itself, like:
Code:
Trigger(label)
Because this would be a good opportunity to mix things up before moving on with beta-testing with the new Marathon version.
 
While testing I've come to realize that by using getGameTurnForMonth() I'm getting the game turn closest to the date, not the first date that is actually past that date.

An example: World War II is scripted to erupt on September 1939. Now, lets say that turn #501 is closest to this date (~ June 1939) - so the Trigger that sets things in motion would fire before the historical date.

This is not how PyScenario worked before, where the Trigger would have fired on the turn following September 1939 - on turn #502. It could be December 1939 or January 1940 or whatever, but the actual event would appear to have happened in-between the turn before the date and the turn after the date.

This is also a issue of Triggers firing at the end of a game turn - not at the beginning of the game turn.

I don't like this outcome much myself. What do the beta-testers think?

Maybe its just a matter of adding one month to the conversion from years to months? Like:
Code:
iTurn = getTurnForMonth(iYear * 12 + 1)
And getTurnForMonth is defined as:
Spoiler :
Code:
def getTurnForMonth(iMonth):
    return getGameTurnForMonth(iMonth, Game.getStartYear(), Game.getCalendar(), Game.getGameSpeedType())
 
If it fires at 6.1939 doesn't it really happen between turns 501-2? so 6.39 is the last turn of peace and 1.40 is first of war (because of events happening at the end of turn)? If so the problem is to represent sth that happened at 10.39, because game rounds up the date to turn 502 (1.40) and fires at the end of it so event triggers a turn too late.
 
This is really confusing, so I'll just try to get it right and get back to you once I actually know what is what.
 
Ok, this is what I regard to be the Problem (with a capital P):

Lets say that I make a Trigger that shows a popup message telling the player about some historical event that supposedly happened in the year 2800 BC. In an actual game the game (Normal speed) turn #6 shows the date 2820 BC so as the designer of this particular scenario I would be expecting the popup any time now, so I click "End Turn" in anticipation.

The next game turn (#7) is 2790 BC but there is no popup - something must be wrong? Well, not "wrong" really, but still messed up. Because this game turn is nearest to 2800 BC the popup will show at the end of this turn instead.

But this is no good, because if I press "End Turn" one more time the date changes to 2760 BC as the pupup is launched. But since the text is describing an even that supposedly took place 40 years ago, this scenario is pretty broken. The popup header "2800 BC" also makes no sense what so ever and will only add to the confusion.

This way of triggering on year dates is simply counter intuitive and will be an obvious "bug" report from every single PyScenario user, from now on and always. (It doesn't matter how much I try to document something like this.) And I don't personally want such a setup for my own scenario making either, where historical events would appear to take place up to 40 years too late! :eek:

I'm seeing three possible solutions for this issue:

  1. Not convert PyScenario to RFC Epic/Marathon at all. Then there is no problem.
  2. Figure out how to launch Triggers at the beginning of a game turn instead of at the end of the game turn (which will appear to take place at the beginning of the following game turn). Then the rounding of years would actually make some sense.
  3. Only allow for one game speed for use with PyScenario - namely Marathon. (But it would be as easy as to change one value in PyScenario.py to change the default speed to another one.) The turn() method would still be the default way of triggering historical events, and the year() method would work as it does now.

Any other suggestions before I make up my mind?
 
It's counter intuitive but I don't think it is a problem (nor Problem). For sure it's not big enough to use the first option. I think that even third one is too harsh. So I suggest second option or find the other way to fix it. The simplest method (IMHO) is:
Code:
iTurn = getTurnForMonth(iYear * 12) - 1
 
Wouldn't that solution create odd results in the opposite direction? What if there was a actual game turn dated 2800 BC, then the event would happen too soon? Or I'm a lost in math, once again?
 
Turns go: -2820(#6), -2790(#7). Event date: -2800. getTurnForMonth(iYear * 12)==7. iTurn==6 Event fires at the end of -2820(#6) so player sees it at -2790- the closest possible date.
Other case: Event date: -2810. getTurnForMonth(iYear * 12)==6. iTurn==5 Event fires at the end of -2850(#5) so player sees it at -2820- yet again the closest possible date.
Buggy outcome could happen if player wants to create "map changes" like Sahara PyScenario, because year -3000-> iTurn==0 so maybe sth like:
Code:
iTurn = getTurnForMonth(iYear * 12) - 1
if iTurn == 0:
iTurn=1
Or iTurn==0 is valid?
 
I don't think that simply firing year() one turn earlier as default would do it. Because I also tried 2900 BC and 2700 BC - and both appeared to fire when appropriate. So fixing 2800 BC by moving the event one turn earlier would brake the other outcomes, then.

I realized however that if I leave year() as it is today (with version 1.031) it should work as I want it to. But I don't know how to implement firing Triggers on fractional years (in the late game on slower game speeds). Perhaps it would be possible to have a month() method for those cases when a year is made up of several turns? But I'm not sure how to check something like that... :p

The first turn is indeed 0 (zero) so its not a problematic value at all. But you can't use turn() or year() to fire something before the first turn - you need the startup() for that. But such changes should really be done in the WBS instead.
 
I've noticed that when I spawn a city that is on a tile adjacent to both the sea and a river, the river on that tile is erased.

E.g. If I spawn Danzig, the river tile directly east of it will disappear. =/

I find this very frustrating when I spawn cities in Europe, as it is a continent full of rivers!
Is there any way to prevent this or add in the river tile afterwards?
Thanks!
 
I've noticed that when I spawn a city that is on a tile adjacent to both the sea and a river, the river on that tile is erased.
This is a known bug caused by me using the wrong line of code and it will be fixed with the next release.

Thank you for pointing it out none-the-less. :goodjob:
 
Because I also tried 2900 BC and 2700 BC - and both appeared to fire when appropriate.
:o Are you sure? Because before reporting you this bug I tested it (and just I've tested it again) and for example Trigger(0).year(-2970) fires in 2970, at the end of turn so it appears at the beginning of 2940.
BTW: oh yeah, here again Python counts beginning with 0, so year(-3000) would be iTurn==-1 so:
Code:
iTurn = getTurnForMonth(iYear * 12) - 1
if iTurn <= 0:
iTurn=0
 
:o Are you sure? Because before reporting you this bug I tested it (and just I've tested it again) and for example Trigger(0).year(-2970) fires in 2970, at the end of turn so it appears at the beginning of 2940.
I tested 3000 BC, 2900 BC, 2800 BC and 2700 BC on Normal speed. Both 2800 BC and 2700 BC fired too late to make any sense. (Well, 3000 BC actually happened at the end of 3000 BC so it showed up in 2970 BC... But it did happen on the first turn - turn #0 - so I wouldn't consider it "broken". So it was really only 2900 BC that fired when it made any sense - in 2880 BC.)

So no matter what you do with this rounding business some date will always appear to to be wrong. I don't this its useful, at all...
 
2900BC in the case made "any" sense firing at 2880BC. But (in my opinion) it would be better if it fires at 2910BC (because it is the closest date). Furthermore, it fixes all "broken" dates, so... OK, the one not fixed would be 3000BC but it's not a big deal, is it? My advice: test my solution and tell me if it's OK.
 
Ok, I tested and this is what I got:

iYear = -3000 did of course not fire at all, but that could be fixed, i guess.
iYear = -2900 fired at 2910 BC
iYear = -2800 fired at 2790 BC
iYear = -2700 fired at 2700 BC

So its much better but still not perfect. Hopefully you understand why I don't want something that is less good than what we have now. Because the idea is to improve on PyScenario.

The only reason for not using the present way of identifying game turns in PyScenario is that there are fractional dates in the late game on slower game speeds. But scenarios are designed for use with Normal speed, right? So there are no fractional dates to detect.

So I could just ignore the whole issue of having Triggers fire on a specific month. A Trigger that fires only once will fire on the first game turn showing the actual date. And a Trigger that fires more than once will fire on every turn showing that date. As simple as that.

If someone can think of a way to figure out what month is showing in game I can add a month() method. Otherwise you pretty much have to resort to using the turn() method and restricting your mod to one game speed only. (Otherwise those Triggers will fire completely off.)

I just don't know at this point.
 
So its much better but still not perfect. Hopefully you understand why I don't want something that is less good than what we have now.

What is worse: 3000BC event doesn't fire
What is DIFFERENT: events fire at the closest possible date to date in trigger
What is better: the system allows to create events in late-game scenarios

In my opinion the new system has more pros than cons. I think we (you) should implement the new system and, in meantime, try to figure out how to fix it.
 
I'm figuring out the fix as we speak...

But I don't think I'll ever go for Triggers that fire before the actual date. That would just result in a infinite cause of bug reports from users who think that PyScenario is broken. Any alternative is better, for me. (And I need make a application that I wanna use myself also.)
 
Back
Top Bottom