...per faction, eg up to 31 times per turn. But it is indeed one of the very low rate firing events.the monitors for a settlement fire only once a whole turn
Back up your campaign_script and then delete all monitors, get a benchmark for the AI turn times with no monitors, then add a few thousand monitors with events that never happen (like GeneralJoinCrusade in a mod with no crusades) and see what happens. The AI turn times will increase in direct proportion to the number of monitors you added. It's not the effects firing within the monitors that slow AI turn times, it's the sheer number of monitors. I'm sure the effects have some minor impact on AI turn times, but it's completely dwarfed by the impact from monitors themselves.
PS. I don't think if-statements have much impact on AI turn times at all. You can add a few thousand of them to a single monitor with all conditions met and there will be virtually no impact on turn times. Likewise for triggers.
Last edited by Callistonian; March 05, 2022 at 08:44 AM.
IF loops have absolute no impact: the recruit limit script I wrote for EBII has 25K lines with literally thousands of them but has only 30 monitors - no impact on turn times at all.
Personally I think it's the number of active monitors (in memory) that effect the turn times and not the number of times the monitors get called\tested. Theoretically several CharacterTurnEnd events with a thousand characters on the map should bog down the turn times, but afaik it doesn't.
If you look at a standard log you will notice at the end a list of all events and how often they got triggered. This is a list from a number of files (script, standing, advisor, show me etc - which file uses BattleWallsBreachedByEngine, advisor?) and will give you an idea what processing time to expect.
Rule of thumb - once you reach 1K event monitors the molasses starts, earlier if you use condition monitors. So having 199 monitors for this is a decision you may want to rethink.
A bit of reminiscing: there is a financial script (Karl the tax man) in the tutorial section that actually works. It would be the holy grail of scripting were it not for the number of monitors - it totally bogs down the turn times.
I concur that the number of times a monitored event happens doesn't affect turn times: you can have 1,000 characters and one CharacterTurnStart monitor and you will see short AI turns, but switch it around so that you have 1,000 monitors and 1 character (or even 0 characters) and you will get long turns. It's the number of active monitors, terminated monitors don't affect the turn times; however, it's best practice to reduce the total number of monitors even if your monitors are terminating (because they will be active until they self-terminate). Ideally, we would have only one monitor for each needed event type and then use if-statements and calls within those monitors. Unfortunately, event conditions (the ones that don't start with I_) make it impossible to fully realize this ideal.
The Karl the Taxman script is a good example of this - it requires a bunch of monitors for the same event, PreFactionTurnStart, because the event condition Treasury is needed to check treasury values. That script can be made a lot more efficient by using 31 PreFactionTurnStart monitors for each faction to set counters and then using if-statements in the Taxman script's monitors to check whose turn it is, but you would still need separate monitors for the treasury intervals.
I have been preaching 'good housekeeping' for years: terminate monitors wherever possible. Like when the monitor tests for england being the player faction you use an IF loop testing and terminating if the player is not england, or when a monitor will not execute again (eg turn based).
And create event counters for conditions (for use in IF loops) that get used repeatedly - the 'faction counter' set up is a good example of this as Callistonian mentioned.
To paraphrase something Withwnar once told me: "terminating monitors is overrated". I didn't appreciate what he meant at the time, it seems obvious that you should terminate monitors as soon as you can, but what he was referring to is the design paradigm that it's better to have a smaller number of non-terminating monitors than to have a larger number of terminating monitors because terminating monitors are active until they terminate themselves (which sometimes takes a really long time if it ever happens at all). I see a lot of mods doing this - monitoring the same events with similar conditions using multiple redundant terminating monitors. For example, an event-counter-based function call in CS usually looks like this:
But it should instead look like this, putting all function calls inside a single non-terminating monitor:Code:monitor_event EventCounter EventCounterType counter_a and I_EventCounter counter_a == 1 ;do stuff terminate_monitor end_monitor monitor_event EventCounter EventCounterType counter_b and I_EventCounter counter_b == 1 ;do other stuff terminate_monitor end_monitor
Using this paradigm, I reduced the number of monitors in my mod from some thousands to just a couple hundred and the difference in turn times is dramatic.Code:monitor_event EventCounter TrueCondition if I_EventCounter counter_a == 1 set_event_counter counter_a 0 ;do stuff end_if if I_EventCounter counter_b == 1 set_event_counter counter_b 0 ;do more stuff end_if end_monitor
A similar design is the 'calendar script' for events based on turn numbers:
Combining monitors wherever possible is certainly the way to go for a shorter turn time, beats terminating individual monitors hands down.Code:monitor_event FactionTurnStart FactionIsLocal if I_TurnNumber = 0 campaign_wait 1 historic_event 1618_01 end_if if I_TurnNumber = 1 campaign_wait 1 historic_event 1618_01a event\fenstersturz.bik end_if if I_TurnNumber = 2 campaign_wait 1 historic_event 1618_01b end_if if I_TurnNumber = 3 campaign_wait 1 historic_event 1618_01c end_if if I_TurnNumber = 4 campaign_wait 1 historic_event 1618_05 end_if if I_TurnNumber = 5 campaign_wait 1 historic_event held_mansfeld factions { russia, turks, moors, sicily, scotland, byzantium, } historic_event achtzig_krieg ;--- Spawn des Generals bei 303/170 - Pilsen --- spawn_army faction spain character Peter_Ernst_II von_Mansfeld, named character, age 38, x 303, y 170, family, label vonMansfeld1, portrait mansfeld, hero_ability Light_of_the_Faith traits GrafvonMansfeld 1 , GoodCommander 2 , Intelligent 1 , ReligionStarter 1 unit 1648 Bodyguard exp 1 armour 0 weapon_lvl 0 unit Pikeniere exp 1 armour 0 weapon_lvl 0 unit Pikeniere exp 1 armour 0 weapon_lvl 0 unit Pikeniere exp 1 armour 0 weapon_lvl 0 unit Pikeniere exp 1 armour 0 weapon_lvl 0 unit Musketiere exp 1 armour 0 weapon_lvl 0 unit Musketiere exp 1 armour 0 weapon_lvl 0 unit Kuerassiere exp 1 armour 0 weapon_lvl 0 unit Kuerassiere exp 1 armour 0 weapon_lvl 0 unit Leichte Kanone exp 1 armour 0 weapon_lvl 0 unit Moerser exp 1 armour 0 weapon_lvl 0 end end_if
Edit: TrueCondition is totally underrated for stuff like this
Thanks guys, for advice! This is very clear message! I will follow it in the SSHIP - fortunately Belovese made a cleaning in code, lowering the turn time from like 1:15 to something like 0:40 - perhaps by limiting the number of monitors.
Advice for the modders of the SSHIP will be:
Code:;------- OPTIMISATION of processing time:; The goal is to shorten the processing time between the turns, so: --------- MINIMISE NUMBER of ACTIVE MONITORS ------------ ; They are stored in memory and linger there. ; Each monitors should be terminated as soon as it is possible. Add terminate to any monitor, just in case something strange happens. ; The number of times a monitor get called\tested seems not to matter (eg. several CharacterTurnEnd events with a thousand characters on the map should bog down the turn times, but it don't seem to). ; Maybe it'always better to put the conditions that have the least likelyhood at the beginning of a list of conditions in a monitor (because the engine will then just skip the rest of the conditions).
@Jurand - I would just replace your CS recommendations entirely with what Gigantus said: "Try whenever possible to combine monitors with the same event through IF sections."
If the goal is to minimize AI turn times, then we must adopt a new scripting paradigm which emphasizes event-based scripting over terminating monitors. Terminating monitors is overrated because it gives you false hope that you're improving performance when in most cases those monitors will take a long time to terminate or never terminate at all as in your SettlementPanelOpen monitor which is supposed to terminate for the AI but never does because the AI can't open panels. Instead of writing new monitors for whatever script you're working on and throwing in some termination conditions, you should try to work within the existing monitors - divide the CS file based on event types instead of script goals. The only time you should have monitors for the same events is when you absolutely need to test different event conditions (the ones that don't start with I_), and you will find that for most of these monitors it isn't desirable or even possible to terminate, as with your SettlementName condition.
@Gigantus - Putting the additional event condition after the if-statement is bad syntax - CS will fail to parse. Your script should be a 'master' SettlementSelected TrueCondition monitor with a bunch of I_SettlementSelected if-statements and no termination.
@Callistonian - yep, absolutely right. It's the EBII style - they have like 900 monitors. In the Titanium (an alternative SS mod to SSHIP), there's 2600 monitors, in Bulat Steel 1400, in Divide and Conquer 1260. In SSHIP there were 900, but after I've added 2 x 200 while packing a number of the old ones into the new, there're now 1100.
On my new script: I think this one will terminate in the first turn for all-but-Poland factions?
Code:monitor_event SettlementSelected I_EventCounter is_the_player > 0 log -------------------------------------------------------------------- POLAND 0 STARTING INFO if I_IsFactionAIControlled poland terminate_monitor end_if if I_TurnNumber > 1 ; shows up on turn 3 (Summer 1133) disable_cursor hide_ui zoom_strat_camera 1 campaign_wait 0.2 snap_strat_camera 219, 205 campaign_wait 0.5 move_strat_camera 219, 212 point_at_settlement Krakow campaign_wait 0.5 move_strat_camera 194, 222 point_at_settlement Poznan campaign_wait 0.5 move_strat_camera 207, 236 point_at_settlement Gdansk campaign_wait 1 ui_flash_stop show_ui enable_cursor campaign_wait 0.2 historic_event POLAND0_STARTING_INFO terminate_monitor end_if end_monitor
As to your last point in the list - it's not the number of tested conditions that maketh the monitor go achugging, it's the event itself. In other words: you could have a monitor without conditions and commands and it will still cause a delay.
Maybe add this point: Try whenever possible to combine monitors with the same event through IF sections.
As to combining: using "FactionTurnEnd FactionType slave" is a very common monitor to set and test all sorts of counters, just for giggles have a look how many of those you got. Pretty much the same with "PreFactionTurnStart I_FactionIsLocal"
Yeah... is there a way to terminate a following monitor without player clicking on the settlement? If a player is Egypt, then this monitor is likely to linger for the whole game...
Code:monitor_event SettlementPanelOpen SettlementName Krakow log -------------------------------------------------------------------- POLAND 0 STARTING INFO if I_IsFactionAIControlled poland terminate_monitor end_if if I_TurnNumber > 0 disable_cursor hide_ui zoom_strat_camera 1 campaign_wait 0.2 snap_strat_camera 219, 205 campaign_wait 0.5 move_strat_camera 219, 212 point_at_settlement Krakow campaign_wait 0.5 move_strat_camera 194, 222 point_at_settlement Poznan campaign_wait 0.5 move_strat_camera 207, 236 point_at_settlement Gdansk campaign_wait 1 ui_flash_stop show_ui enable_cursor campaign_wait 0.2 historic_event POLAND0_STARTING_INFO terminate_monitor end_if end_monitor
I don't see how it would stay active for any faction: the IF section terminates and the panel will close eventually when the player moves on. And AI factions do not open settlement panels.
Open panel for Krakow
Check out historic event
monitor terminates
Close panel to carry on playing
On the other hand it's unlikely that a faction other then Poland will actually open the panel. And without opening the panel you can't terminate the monitor.
Can't use the I_ScrollOpen condition, obviously. Else this would be easy. Change to the SettlementSelected event? It exports a faction as well.
monitor_event SettlementSelected TrueCondition
if I_IsFactionAiControlled poland
terminate_monitor
end_if
and SettlementName Krakow
do your stuff
terminate_monitor
end_monitor
Not sure if conditions after an IF section work, bit rusty.
It will terminate as expected once the player clicks on the first settlement and is not playing Poland. The 'is player' event counter is superfluous - only the player can select settlements, TrueCondition would have done the job just as fine.
i thought as much - which means the settlement name condition (no I_xyz here) will have to replace the true condition part. Which in turn means the monitor will stay 'live' until someone clicks on the settlement - back to square one.Putting the additional event condition after the if-statement is bad syntax - CS will fail to parse.
Guess the best would be to rewrite it as a turn based (info) event** for the poland player, aka drop it into a calendar script and have it pop up at a chosen turn start.
** suggest add_event method to add a locator (position line) button
Edit: now why did I not think of the I_SettlementSelected condition? Thanks Callistonian.
Agains, thanks to you both!
Actually, it was previously scripted like this. But in practice, there're other events poping out (with a candidate for adoption coming first) that in practice made the whole camera zooming unwieldy. This is the reason I'd prefer the info on the faction to pop out on clicking on a settlement, during the player's turn.
I've actully copied "monitor_event SettlementSelected I_EventCounter is_the_player > 0" from Divide and Conquer code ;-)
But indeed, it's better to use that in if-s part, not as a condition of the whole monitor.
And indeed, checking for the "player" is redundant.
So, is "I_SettlementSelected" working / allowed? I'd be rather surprised. When it would have fired?
BTW, is there a difference between "I_TurnNumber == 2" and "I_TurnNumber = 2" ? (I know, @Gig, you'd prefer > and <)
@Jurand - There is never any reason to use I_ conditions as event conditions. Instead of this:
do this:Code:monitor_event <EventType> I_<condition> ...
This way, you can use this monitor for other stuff as well. I'm guessing your is_the_player counter is set to true only during a human faction's turn (using other monitors elsewhere)? It's a bit pointless to test that here since AI factions can't select settlements - if the SettlementSelected event has fired then it must be a human faction's turn. This looks like a run-of-the-mill faction intro script, so I'm not sure why you're trying to use SettlementSelected in the first place. If you have monitors for every faction (to check whose turn it is), then you can just use those.Code:monitor_event <EventType> TrueCondition if I_<condition> ...
You can do a lot of scripting for the poland faction using this one monitor and event counter function calls to a single EventCounter monitor.Code:monitor_event FactionTurnEnd TrueCondition set_event_counter faction_turn_poland 0 ;set all other faction turn counters to 0 here end_monitor monitor_event PreFactionTurnStart FactionType poland set_event_counter faction_turn_poland 1 if I_IsFactionAIControlled poland set_event_counter ai_status_poland 1 end_if if not I_IsFactionAIControlled poland set_event_counter ai_status_poland 0 if I_TurnNumber == 2 ;display turn 3 historic_event POLAND_STARTING_INFO factions { poland, } ;all your camera stuff end_if end_if end_monitor
I would not look to DAC for a model of efficient scripting. They have Fynn now, and he knows what he's doing, but he's only been around a little while and most of their stuff is old and definitely made under the former paradigm I'm advocating to replace.
I_SettlementSelected is a legitimate condition, it just returns true for the currently selected settlement. You should see it in the docudemons.
There is no difference between = and == as far as campaign_script is concerned, they both test "is equal to". I personally like to use == because = is used for assignment in most programming languages while == is used to check values in conditionals. I.e. in C++:
As far as inequalities go, they're considered safer than equalities especially if you're not sure the variable will ever have the particular value. E.g. if you're incrementing a counter every time any character ends their turn in a settlement, then it doesn't make sense to test if counter == 50, you would want to test counter >= 50 or <= 25 or whatever. But for turn numbers, equality is probably fine, just remember that the internal turn numbers start from 0 instead of 1.Code:int a = 5; //assign integer a the value 5 int b = a*2; if (b == 10) { //test if b has the value 10 cout << b; }
Last edited by Callistonian; March 07, 2022 at 10:51 AM.
Callistonian explained why I prefer > < tokens (especially for counter values), but equally why it isn't an issue (rather superfluous actually) for the turn times condition in a calendar style set up. Different story if you are looking for a repeating event after a certain turn number, never mind that it would be poor scripting to start with as you would normally create a counter value to do that.