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

Thank you very much, will test this as soon as possible :D

EDIT:
I've just noticed a silly thing. What if the name needs to be different depending on who the founder is?
 
Thank you very much, will test this as soon as possible :D
No, thank you! You have a beta-testing credit to call your own. :D

I've just noticed a silly thing. What if the name needs to be different depending on who the founder is?
I guess I could build something that works with the City Name Manager (the Python module that is in charge of all the historical city naming in RFC). Ironically I disabled at one point, and it could have unforeseen consequences if I reverted this change. But I think that I basically could work around the issue and put it back in use. (And even make it work for the minor Civs that have no city name maps of their own.)

Other than that, you could have a Trigger that looks for a city by a certain name - and if it finds it and the tile is owned by some Civ - then the city name changes to whatever it need to be. Like:
Code:
Trigger("found New York").random(a,b,c).city(x,y,"New York")
Trigger("rename New York").fired("found New York").player(c).name("New Amsterdam","New York",(x,y))
The variables x and y refer to the tile coordinates while a, b and c would be ePlayers who are potential founders of New York. The second trigger only fires if the first one has fired and looks for a city called "New York" owned by c. If these conditions are true the city name immediately changes to "New Amsterdam".

But this means that you basically need to add a remaning Trigger for each player (other than the default), so there would no gain with using the random() method...

The name() method is actually quite powerful - lookup the API entry!
 
The random player works! Not tried randomize() yet but will soon. However I've run into another problem. A number of triggers are not firing for me (that used to) so I figure I haven't "updated" to v2 yet. That said, I can't see where I'm going wrong. Can you help?

Code:
#Construction of Arabian mosques and temples in Cairo, Khartoum, Sana'a and Jerusalem (usually don't get these as they flip before selection of Islam as state religion)
Trigger("Spread Islam").date(620).target((69,30),(76,38)).religions(2) #Spread Islam to flipped cities
Trigger("Build Mosques").date(620).target((69,30),(76,38)).buildings(85,86) #Build mosques and temples in flipped cities
As far as I can tell, Islam is getting spread to these cities (or at least, by the time I spawn as Spain, Islam is in each of those cities). However no buildings are built and I can't figure out why.

Code:
#China
Trigger("China Flip").player(2).check().date(650,1050).interval(30,True,True).target((101,39),(107,42)).flip().units(eType=iArcher)

#Japan
Trigger("Edo Flip").player(8).date(620).flip("Edo").units(eType=iLongbowman,iNum=1)

#Arabia
Trigger().player(12).check().date(620).target(76,40).flip().units(eType=iLongbowman,iNum=2) #Baghdad
Trigger().player(12).check().date(620).flip("Tehran").units(eType=iLongbowman,iNum=2)
Trigger().player(12).check().date(620).flip("Sirajis").units(eType=iLongbowman,iNum=2)
Trigger().player(12).check().date(620).flip("Kandahar").units(eType=iLongbowman,iNum=2)
Trigger().player(12).check().date(700).flip("Alexandria")
Trigger().player(12).check().date(750).flip("Hesperides")
Trigger().player(12).check().date(800).flip("Tripolis")
Trigger().player(12).check().date(900).target(58,39).flip() #Tunis
Trigger().player(12).check().date(1000).target(55,39).flip() #Algiers
Trigger().player(12).check().date(1100).target(50,36).flip() #Casablanca
None of these are happening. Originally the player() field was in the form player(iArabia) so I changed everything to integer numbers and no difference (it was a long shot anyway). I tried removing the check() but no change (since I wasn't playing as any of these civs anyway). I'm not sure what I am doing wrong.

Code:
### STABILITY TRIGGERS
#Arabian instability near 13th century
Trigger("Arabian Instability").player(12).check().date(1150,1350).interval(20,True,True).stability(-15)
I don't know how to check for this, but as far as I can tell this one isn't happening either, since in most autostarts I set up as America, I see Arabia has survived! However I can accept this might be because the interval I have set up allows for the possibility of no stability penalties if Arabia is lucky.

Code:
#Free random colonies
#Cape Town
Trigger().player(16).check().valid(bForeign=True).date(1500,1700).interval(65,True,True).city(63,10,iSize=3).name("Cape Town").culture(50).buildings(lColonialBuildings).religion(iChristianity).units(eType=iMusketman)
Trigger().player(19).check().valid(bForeign=True).date(1500,1700).interval(65,True,True).city(63,10,iSize=3).name("Kaapstadt").culture(50).buildings(lColonialBuildings).religion(iChristianity).units(eType=iMusketman)
#Winburg
Trigger("Spawn Winburg").random(16,19).check().valid(bForeign=True).date(1500,1700).interval(25,True,True).city(69,13,iSize=3).name("Winburg").culture(50).buildings(lColonialBuildings).religion(iChristianity).units(eType=iMusketman)
#Colombo
Trigger("Spawn Colombo").random(16,19,21).check().valid(bForeign=True).date(1600,1730).interval(20,True,True).city(93,29,iSize=3).name("Colombo").culture(50).buildings(lColonialBuildings).religion(iChristianity).units(eType=iMusketman)
Now your random() feature works fine. However, I've noticed Winburg and Colombo don't spawn :confused:

Cape Town usually appears, however. Originally the valid() filter had bAdjacent=True, but I have since removed it. I'm not sure which setting to leave bAdjacent at. I want the condition to fail if the plot is in foreign lands, but NOT to fail if the plot is adjacent to foreign lands.
 
Ouch, that was a load! Before I try them out myself I just thought I'd check if you are sure that Python exceptions are enabled?

Also, I have disabled exceptions for initialization of the Scenario module by default. You can enable them by changing line #28 in PyScenario.py to:
Code:
bDisplayDebugMessages = True
For testing you script, you can enable cheat mode and start the game as Americans (unlocked scenario). On the first turn, pause the game and hit ctrl + z in order to reveal the whole map. When you un-pause the game the game turns left window is blocking your view, so you open up any advisor and close it again. This makes the original window go away.

Now you can pause the game whenever you want and zoom in on the action. You can see the game date by opening the World Builder (ctrl + w) and gifting the Calendar tech to the Americans.

Also, if you know how to open up the Python console it is possible to run this line of python:
Code:
CyGame().setGameTurn(iGameTurn)
If you substitute the iGameTurn variable with a integer game turn the game jumps to that date. You can also put this in your Scenario module and the game will start on the specified game turn. This takes care of having to wait for the game date to actually change every time.
 
You might also try and set iInterval=1 when you're testing Triggers with the interval(bRandom=True) method. Then, if it works, you can raise the value to see how it plays out. But a probability of 1/20-25 is pretty slim in any case!

Also, do you really want Winburg and Colombo to spawn without rival territory? Because you're using bForeign=True for the valid() Condition.
 
Great news! RFC Epic/Marathon has been updated - but you're not required to use it for testing before the next PyScenario update. embryodead has added some additional Python capabilities to the mod, so this lets me do some cool stuff for PyScenario also.

What about a method for granting bonus Gold/Science/Culture/Espionage to all cities belonging to the Target Player? :king: Could somebody name it already! :D

And lets have a method for dynamically changing the Civ attribute of a Player! Like make the Celtic Civ expire in the 3000 BC scenario and substitute it with Byzantium. Then spawn Byzantium in its place!

I think I'll have to make a proper spawn method for Civs also. One that resets stability, wars, vassals and stuff like that.
 
You might also try and set iInterval=1 when you're testing Triggers with the interval(bRandom=True) method. Then, if it works, you can raise the value to see how it plays out. But a probability of 1/20-25 is pretty slim in any case!

Also, do you really want Winburg and Colombo to spawn without rival territory? Because you're using bForeign=True for the valid() Condition.
A probability of 1/20 seemed fine, since there are ~30 turns in the interval in normal speed (and thus obviously more in Epic/Marathon) I figured most games the city would spawn. Besides, a lot of my triggers without intervals don't work either :(

As for without rival territory. I wanted the valid() condition to make it so that:
Winburg/Colombo do not spawn if the tile already has a city on it.
Winburg/Colombo do not spawn if there exists a city in an adjacent tile.

I set bForeign=True so that the condition would pass if the tile was in foreign lands. Is this wrong?
 
A probability of 1/20 seemed fine, since there are ~30 turns in the interval in normal speed (and thus obviously more in Epic/Marathon) I figured most games the city would spawn.
You can test things any way you want. Personally I don't have the time to actually play entire games in order to test Triggers, so I skip ahead and see if they Trigger instead. With an interval Trigger I would probably just make it very likely to happen in order to verify that the Trigger is valid and will Trigger. Otherwise testing would take forever.

Besides, a lot of my triggers without intervals don't work either :(
There is probably something else wrong with your script. Could you post/email the whole thing? It shouldn't be hard to spot the error - and I might also learn something vital in the process. :D

I set bForeign=True so that the condition would pass if the tile was in foreign lands. Is this wrong?
This would be correct. :goodjob:
 
I will send you an email shortly. I am sure the interval method works however, since Panama spawns in the correct timeframe. To test I usually just start a game as America and observe what happens whilst I'm not there :)

EDIT: Right, I have found the problem!
I had exceptions enabled in the CivIV.ini HOWEVER I didn't know I also had to enable it in pyscenario.py until you told me.

I have found the error I was making. Civ was kind enough to point me towards lines 113, 114, 116 and 120. I am quite embarassed but I'll let you see my mistake...

Code:
#Free random colonies
#England/Netherlands
#Cape Town
Trigger().player(16).check().valid(bForeign=True).date(1500,1700).interval(65,True,True).city(63,10,iSize=3).name("Cape Town").culture(50).buildings(lColonialBuildings).[COLOR="#ff0000"]religion[/COLOR](iChristianity).units(eType=iMusketman)
Trigger().player(19).check().valid(bForeign=True).date(1500,1700).interval(65,True,True).city(63,10,iSize=3).name("Kaapstadt").culture(50).buildings(lColonialBuildings).[COLOR="Red"]religion[/COLOR](iChristianity).units(eType=iMusketman)
#Winburg
Trigger("Spawn Winburg").random(16,19).check().valid(bForeign=True).date(1500,1700).interval(25,True,True).city(69,13,iSize=3).name("Winburg").culture(50).buildings(lColonialBuildings).[COLOR="#ff0000"]religion[/COLOR](iChristianity).units(eType=iMusketman)

#England/Netherlands/Portugal
#Colombo
Trigger("Spawn Colombo").random(16,19,21).check().valid(bForeign=True).date(1600,1730).interval(20,True,True).city(93,29,iSize=3).name("Colombo").culture(50).buildings(lColonialBuildings).[COLOR="#ff0000"]religion[/COLOR](iChristianity).units(eType=iMusketman)
That's right. A typo. Since the spawns were similar I copy/pasted and therefore the typo is in the file four times. :crazyeye:

Interesting things I found. All city spawns still worked despite the exceptions. City flips however, did not! In my scenario, city spawns that worked were placed before line 113, and anything in the code that didn't work (city renaming, resource spawns, other triggers) was placed after. Now I don't know anything about programming, but I think I have deduced this about how your code works.

The game loads all triggers at game start, whether it is their "turn" to be fired or not. If a trigger is faulty it just doesn't get loaded at initialisation, and any triggers placed after it in the scenario file don't get loaded either.

However any trigger that did get loaded will still fire at the appropriate date. If I had a python exception in line 194, say (the last line in my file), I probably wouldn't even have noticed! :p
 
Your assumption sound likely to me. This is not supposed to happen though. As any script which produces exceptions should produce no Triggers (the list of Triggers is wiped clean). The Python Debug log will still have entries for these Triggers being initialized.

I'll write up this as an issue and figure out how to fix it in the next update.

edit: Looking at the code I now realize that the disable-Triggers-on-exception functionality is only in place for reloading Triggers after initialization - not on actual initialization. This means that I have to rewrite the whole section on loading Triggers. Its probably for the best as the implementation isn't all that great to begin with.
 
Also, I've been toying with the idea of making my own "error messages" for loading Triggers. Ones that actually make some sense to the user. With hints on what sort of error could be in the actual script. Like "check spelling", "check quotation marks", et cetera. But this would take some time for testing and development, so it might not get done in awhile... :rolleyes:
 
hi! I've updated my pyscenario and downloaded the last version of rfc marathon.
I changed all the year() functions with the new date() from my previous work, and also removed all the label() and added the player() functions...

But now...nothing seems to work ç_ç i can't find where the problem is...
 
If you post your script (or email it to me) I would probably be able to spot the error. Otherwise there are some suggestions for conversion in this thread.
 
Also, I've been toying with the idea of making my own "error messages" for loading Triggers. Ones that actually make some sense to the user. With hints on what sort of error could be in the actual script. Like "check spelling", "check quotation marks", et cetera. But this would take some time for testing and development, so it might not get done in awhile... :rolleyes:
It is more than good enough for now that the game tells you what line in the file the exception occurs in. All good text editors have numbered lines so this makes it somewhat easier :)

hi! I've updated my pyscenario and downloaded the last version of rfc marathon.
I changed all the year() functions with the new date() from my previous work, and also removed all the label() and added the player() functions...

But now...nothing seems to work ç_ç i can't find where the problem is...
I'd say post the script, then I might be able to offer some help also :)
 
PyScenario is now exclusively designed for the mod-mod RFC Epic/Marathon! This is blah blah blah... PyScenario is no longer compatible with the official version of RFC.

Damn you...
 
Damn you...
What exactly are you complaining about?

The reason PyScenario is only available for one select mod at this point is that it is still in beta-testing. (And the RFC crowd is pretty much into scripting historical events.) If you help out with testing it might get a proper release for the core game that much sooner. Fair enough?
 
Besides, RFCMarathon set at "normal" speed is exactly the same as the base game - it even runs faster thanks to some optimisations! Even if you don't like Marathon/Epic speeds, downloading this modmod is still a good idea. Right?
 
If you can point me in the direction of a Warlords-compatible version of RFC Marathon/Epic, I'd be on that faster than a Catholic priest on an altar boy.

EDIT: as long as new game speeds are being added to RFC, can we put all of the old difficulty levels back in too, or at least Noble? The three difficulties that come with the mod all have screwed-up research and construction costs.
 
Oh, that wouldn't work since PyScenario is done with BTS. So is RFC Epic/Marathon.

You basically need to get BTS and forget about Warlords, at this point. Seriously. :rolleyes:
 
Oh, that wouldn't work since PyScenario is done with BTS. So is RFC Epic/Marathon.

You basically need to get BTS and forget about Warlords, at this point. Seriously. :rolleyes:

I hate you so very, very much...

RFC comes in regular, Warlords, and BtS versions. How hard would it be to make Epic/Marathon and PyScenario for those versions?
 
Back
Top Bottom