Script optimization

Post » Wed Dec 09, 2009 7:24 am

I'm getting ready to add schedules to my NPC's using the MWScheduler tool

I'm interested in learning how best to optimize these.

I believe LCOV used activators in each cell to schedule NPC's - I can understand that they didn't want to load scripts against individual NPC's however was this also done to optimize the scripts?

As I have unique NPC's which are not impacted by other mods is it better for me to attach the individual schedules to each NPC or do a group script based on the cells they primarily inhabit.

The schedules are unique for each NPC and include what they wear during different times of the day and various cells they can be positioned in.

Any advice/thoughts welcome
User avatar
kevin ball
 
Posts: 3399
Joined: Fri Jun 08, 2007 10:02 pm

Post » Wed Dec 09, 2009 10:32 pm

One script is better than a bunch of small ones, I'd say. Only have one iteration per game loop as opposed to as many iterations as there are NPCs.
User avatar
Trey Johnson
 
Posts: 3295
Joined: Thu Oct 11, 2007 7:00 pm

Post » Wed Dec 09, 2009 3:06 pm

To my knowledge noone has done any benchmarking of which is faster although I would be temped to make the same assumption as Trunksbomb.
User avatar
Margarita Diaz
 
Posts: 3511
Joined: Sun Aug 12, 2007 2:01 pm

Post » Wed Dec 09, 2009 3:50 pm

Thanks both for the replies - instead of using individual scripts I have grouped my first set of NPC's into a central script and attached to an activator

Am using Grumpy's fairly straight forward template which goes like this

Begin CT_External_Dunhaven_schedshort counterif ( MenuMode == 1 )  returnendifif ( counter < 60 )  set counter to counter + 1  returnendifset counter to 0if ( "CT_Starkjente"->GetLOS Player == 0 )  if ( "CT_Starkjente"->GetHealth > 0 )  if ( GameHour > 18 )	"CT_Starkjente"->disable  elseif ( GameHour < 6 )	"CT_Starkjente"->disable  else	"CT_Starkjente"->enable  endif  endifendif


Is there anything else I could do to optimise this script?
User avatar
Pat RiMsey
 
Posts: 3306
Joined: Fri Oct 19, 2007 1:22 am

Post » Wed Dec 09, 2009 1:48 pm

Is there anything else I could do to optimise this script?

Maybe better:

Begin CT_External_Dunhaven_schedshort counterif ( MenuMode == 1 )  returnendifif ( counter < 60 )  set counter to counter + 1  returnendifset counter to 0if ( "CT_Starkjente"->GetHealth <= 0 )  returnendifif ( "CT_Starkjente"->GetLOS Player == 0 )  if ( GameHour >= 6 )  if ( GameHour <= 18 )	"CT_Starkjente"-> Enable  else	"CT_Starkjente"-> Disable  endifelse	 "CT_Starkjente"-> Disable		endifend

User avatar
Laura Cartwright
 
Posts: 3483
Joined: Mon Sep 25, 2006 6:12 pm

Post » Wed Dec 09, 2009 6:24 am

Thanks Alan - I think I will put the gethealth first with a seperate return

I was having problems with the gamehour in testing and for some bizarre reason it will only work if I put the largest hour first - so do the test for 6pm then the one for 6am

Am thinking I might follow the Lcov path and have a master script that calls the local scripts depending on what hour range is working something like this

begin SC_SeydaNeen_MasterdontSaveObjectif ( menuMode )	returnelseif ( cellChanged )	set SC_offScheduleG to 0elseif ( SC_SeydaNeen_State == 0 )	returnelseif ( SC_Reschedule )	set SC_SeydaNeen_State to -1	set SC_Reschedule to 0elseif ( gamehour < 7 )	if ( SC_SeydaNeen_State != 4 )		set SC_Reschedule to 0		set SC_SeydaNeen_State to 4		startScript SC_SeydaNeen_4	endif	startScript SC_SeydaNeen_C4	returnelseif ( gamehour < 12 )	if ( SC_SeydaNeen_State != 1 )		set SC_Reschedule to 0		set SC_SeydaNeen_State to 1		startScript SC_SeydaNeen_1	endif	returnelseif ( gamehour < 19 )	if ( SC_SeydaNeen_State != 2 )		set SC_Reschedule to 0		set SC_SeydaNeen_State to 2		startScript SC_SeydaNeen_2	endif	returnelse	if ( SC_SeydaNeen_State != 3 )		set SC_Reschedule to 0		set SC_SeydaNeen_State to 3		startScript SC_SeydaNeen_3	endif	startScript SC_SeydaNeen_C3	returnendifend

User avatar
Sakura Haruno
 
Posts: 3446
Joined: Sat Aug 26, 2006 7:23 pm

Post » Wed Dec 09, 2009 4:01 pm

I'm not sure, but I'd definitely turn this block inside out, like so:
if ( GameHour >= 6 )  if ( GameHour <= 18 )		if ( "CT_Starkjente"->GetLOS Player == 0 )  			"CT_Starkjente"-> Enable		else			"CT_Starkjente"-> Disable		endif	else		"CT_Starkjente"-> Disable			endifendif


Doing number comparisons should be a lot faster than a whole line-of-sight test, so put the fastest checks and most likely to fail on the outside (the slowest ones are then evaluated less often).
User avatar
Rowena
 
Posts: 3471
Joined: Sun Nov 05, 2006 11:40 am

Post » Wed Dec 09, 2009 6:44 am

I was having problems with the gamehour in testing and for some bizarre reason it will only work if I put the largest hour first - so do the test for 6pm then the one for 6am

The condition has not been truly set. I faced with it too.

I'm not sure, but I'd definitely turn this block inside out, like so:

But then need:
if ( GameHour >= 6 )  if ( GameHour <= 18 )		if ( "CT_Starkjente"->GetLOS Player == 0 )  			"CT_Starkjente"-> Enable		endif		else		  if ( "CT_Starkjente"->GetLOS Player == 0 )  			"CT_Starkjente"-> Disable		  endif		endif	else	  if ( "CT_Starkjente"->GetLOS Player == 0 )		"CT_Starkjente"-> Disable	  endif	endifendif

I'm not sure, that it not hit FPS more.
User avatar
Chris Duncan
 
Posts: 3471
Joined: Sun Jun 24, 2007 2:31 am

Post » Wed Dec 09, 2009 5:36 pm

Is there a reason GetLOS has to be there at all? Won't the character have to be there or not regardless of which side PC looks?
User avatar
Lil'.KiiDD
 
Posts: 3566
Joined: Mon Nov 26, 2007 11:41 am

Post » Wed Dec 09, 2009 12:39 pm

But then need:
-snip-
I'm not sure, that it not hit FPS more.

No, you'd need:
if ( GameHour >= 6 )	if ( GameHour <= 18 )		if ( "CT_Starkjente"->GetLOS Player == 0 )  			"CT_Starkjente"-> Enable		else			"CT_Starkjente"-> Disable		endif	else		if ( "CT_Starkjente"->GetLOS Player == 0 )			"CT_Starkjente"-> Disable		endif	endifendif


Possibly even simpler.
If MW respects flow control in the least, that will definitely be a lot faster.

Is there a reason GetLOS has to be there at all? Won't the character have to be there or not regardless of which side PC looks?

So the NPC doesn't disappear/appear while the player is looking right at them.
User avatar
Emma Copeland
 
Posts: 3383
Joined: Sat Jul 01, 2006 12:37 am

Post » Wed Dec 09, 2009 2:20 pm

So the NPC doesn't disappear/appear while the player is looking right at them.
Then the script above doesn't do what it is intended for.

set StarkEnable to 0if ( GameHour >= 6 )  if ( GameHour <= 18 )		if ( StarkEnable == 1 )			return		else			set StarkEnable to 1		endif  else		if ( StarkEnable == 0 )			return		else			set StarkEnable to 0		endif  endifelse  if ( StarkEnable == 1 )		return  else		set StarkEnable to 1  endifendif; the script reaches this point no more than three times a dayif ( "CT_Starkjente"->GetLOS Player == 1 )    returnendifif ( StarkEnable == 0 )		"CT_Starkjente"->Disableelse		"CT_Starkjente"->Enableendif


It might be better just to check time once at CellChanged. Does the NPC have to blink in/out even when PC turns away just for a second?
User avatar
Dale Johnson
 
Posts: 3352
Joined: Fri Aug 10, 2007 5:24 am

Post » Wed Dec 09, 2009 8:01 pm

Then the script above doesn't do what it is intended for.

*snip*

It might be better just to check time once at CellChanged. Does the NPC have to blink in/out even when PC turns away just for a second?


Um... forgive me ... I really appreciate the posts but it takes me a while to work through the suggestions and try and assemble the logic of the code in my mind

The original code is Grumpy's and I had been reading about it in an archived forum thread http://detritus.silgrad.com/grumpythreads/The%20Elder%20Scrolls%20Forums%20-%20NPC%20scheduleing__.htm

This thread introduced me to max aka nobody's MW scheduler - which I tried and couldn't get the resultant script generation to work - at least the test NPC was being moved to the correct cells but not at the times I expected?

So i went back to grumpy's script - I can get it to work ok for some of my NPC's but not for others - which I think is due to having too many hour variables in the script eg

if ( "CT_Starkjente"->GetLOS Player == 0 )  if ( "CT_Starkjente"->GetHealth > 0 )  if ( GameHour > 18 )	"CT_Starkjente"->disable  elseif ( GameHour < 6 )	"CT_Starkjente"->disable  else	"CT_Starkjente"->enable  endif  endifendifif ( "CT_Ogrul"->GetLOS Player == 0 )  if ( "CT_Ogrul"->GetHealth > 0 )  if ( GameHour < 18 )	"CT_Ogrul"->disable  elseif ( GameHour > 6 )	"CT_Ogrul"->disable  else	"CT_Ogrul"->enable  endif  endifendif


Starkjente will enable and disable at the correct times but Ogrul won't -

I haven't seem Starkjente just disapear in front of me and I know GetLOS acts more as a proximity sensor than checking if you can actually see the object

So I've decided I'll go down a simplified path of LCOV having a master script that checks the hour eg gamehour < 7 and will set a state and run a seperate script for the NPC actions for that time period.

I want to keep the code fast and short - and only want it to run when the cell is entered - if anyone has suggestions on how I achieve that I'd really appreciate it

Once I've set up my master script and a single schedule I'll repost here for advice

Thanks to everyone so far for helping me - I fell like I'm learning how to read all over again - painfully and slowly word by word so your patience is much appreciated
User avatar
Stay-C
 
Posts: 3514
Joined: Sun Jul 16, 2006 2:04 am

Post » Wed Dec 09, 2009 6:47 pm

So i went back to grumpy's script - I can get it to work ok for some of my NPC's
*shrug* Whatever. I thought the purpose of the topic was to make a least resource-demanding script rather than the script most familiar.

Starkjente will enable and disable at the correct times but Ogrul won't -
No wonder. Conditions in the second block are placed in wrong order : if ( GameHour < 18 ), elseif ( GameHour > 6 ) is wrong. If you want Ogrul appear at different time than Starkjente, rearrange only enable/disable commands, not conditions.

I want to keep the code fast and short - and only want it to run when the cell is entered
In such case it's quite simple and doesn't require GetLOS at all.
Begin CT_External_Dunhaven_schedif ( CellChanged == 0 )  returnendifif ( "CT_Starkjente"->GetHealth <= 0 )	"CT_Starkjente"->disable	returnendifif ( GameHour > 18 ); appears only at day	"CT_Starkjente"->disableelseif ( GameHour < 6 )	"CT_Starkjente"->disableelse	"CT_Starkjente"->enableendifif ( GameHour > 18 ); appears only at night	"CT_Ogrul"->enableelseif ( GameHour < 6 )	"CT_Ogrul"->enableelse	"CT_Ogrul"->disableendifend

User avatar
Tarka
 
Posts: 3430
Joined: Sun Jun 10, 2007 9:22 pm

Post » Wed Dec 09, 2009 7:59 am

"'I've made a script, it works but..." the sequel


A bunch of NPC from my Pegasus Estate mod will also be having a schedule to make them move around the house.
To make matter smore complicated, the npcs are first disabled and only appear after a quest has been complete. Some of them also go on missions, so they get diabled again.

I've first looked at MWscheduler (findind this tool make me decided I'd use schedules in the first place) but had the same difficulties as Illy.
After much frustration, I looked at Princess Stomper's Leyawiin scripts (as she suggested in another thread) and found what I needed.

In the end, the scripts work as intended but...

Here are samples of the scripts

On npcs
 Begin aa_alchemist_script    Short doonce        If ( aaHiredAlch == 0 )        If ( doonce == 0 )            Disable            Set doonce To 1        EndIf    Else        If ( doonce == 1 )            Enable             Set doonce To 0        EndIf    EndIf...


On a hidden activator
 Begin aa_PE_Schedule_main; Thanks to Galsiah, Yacoby, bjam and OldeCow69 for the tips. Concepts nicked from Helios, Wrye and COM.            If ( MenuMode == 1 )        Return    Else        If ( GameHour > 23 )            If ( aaScheduleDay != 4 )                Set aaScheduleDay To 4                StartScript "aa_PE_schedule1"                StartScript "aa_PE_schedule2"                StartScript "aa_PE_schedule3"            EndIf        ElseIf ( GameHour < 5 )            If ( aaScheduleDay != 4 )                Set aaScheduleDay To 4                StartScript "aa_PE_schedule1"                StartScript "aa_PE_schedule2"                StartScript "aa_PE_schedule3"            EndIf        ...etc...    EndIf    End


The global script

 Begin aa_PE_schedule3    ; thanks to Galsiah, Yacoby, bjam and OldeCow69 for advice        Short state        If ( aaScheduleDay == 1 )	; morning        If ( state == 0 )	; break up script            "aa_maid"->PositionCell 3994 5568 15943 225 " Vestibule"            "aa_merchant"->PositionCell 152083 -4132 97 315 " Pegasus Grounds"            "aa_miner"->PositionCell 4014 6330 877 280 " Mine"            "aa_necro"->PositionCell 4479 3745 16000 182 " Crypt, Arkana room"            "aa_sailor"->PositionCell 151143 -7242 84 45 " Pegasus Grounds"            Set state To 1            Return        ElseIf ( state == 1 )            "aa_seamstress"->PositionCell 3694 3957 12138 251 " Gold Kanet quarters"            "aa_servicemage"->PositionCell 4053 3731 15000 110 " Syrabane Room"            "aa_smith"->PositionCell 152536 -4918 103 16 " Pegasus Grounds"            "aa_thief"->PositionCell 3732 3894 14907 135 " Great Hall"            Set state To 2            Return        EndIf    EndIf     If ( aaScheduleDay == 2 )	; lunch break        If ( state < 3 )            ...........etc............        StopScript "aa_PE_schedule3"    Set state To 0    End


The glitch I get is this: when the time changes (from night to morning for e.g.) the diabled npcs of the cell (the one with the hidden activator) are kinda visible: at first just a black shape, then with colour. They have no collision but the names do appear. Obviously activating them makes the game crash. Leaving the cell and coming back fixed everything.

I am tempted to use a variable, say aaScheduleControl and add these lines to the main script:

 If ( aaScheduleControl ) == 0    returnendif

and set the variable to one only after all the npcs have been enabled. It's a last resort thing that I haven't even tested yet.

Any way I can fix this, besides having the activator in a cell with no npc, which I don't have?
If you know of some way to streamline the scripts, this is forever and ever welcome too ;)
User avatar
Fanny Rouyé
 
Posts: 3316
Joined: Sun Mar 25, 2007 9:47 am

Post » Wed Dec 09, 2009 10:04 am

I am familiar with the collision-less NPC that upon activation crashes the game, but I have never investigated the exact conditions that trigger the phenomenon - I have always been in too big a hurry to fix the problem. I have associated it with moving NPC using Position and/or PositionCell, but it might be in combination with Enable that prevents the collision from being established until the cell is reloaded. My quick solution is to take the NPC through an additional cycle of disabling and enabling - it appears to work if done in the same frame as the move and initial enabling, but if it does not work for you try waiting a frame.

I am tempted to use a variable, say aaScheduleControl and add these lines to the main script:

If ( aaScheduleControl ) == 0    returnendif


That would be a good idea anyway to increase efficiency, but if you do make certain you write it properly: if ( aaScheduleControl ) == 0.

As for the general efficiency of your scripts I do not think there is much room for improvement, and it is difficult to advise you without knowing the details of some of them. I gather that aaHiredAlch is a global variable. If there is a journal associated with these quests to staff Pegasus Estate I wonder that you do not use a journal index rather than introducing a global variable for this purpose unless it is really necessary for aaHiredAlch to be set back to 0 in order to disable the NPC. If not I would recommend:

Begin aa_alchemist_scriptshort doOnce    if ( doOnce == 0 )    set doOnce to 1    Disableelseif ( doOnce == 1 )    if ( aaHiredAlch == 1 ) ; or if ( ( GetJournalIndex "aa_AlchemistQuest" ) >= 100 )        set doOnce to 2        Enable     endIfendIf

Here is a very small change that might not be appropriate depending on what the rest of the script looks like:

Begin aa_PE_Schedule_main; Thanks to Galsiah, Yacoby, bjam and OldeCow69 for the tips. Concepts nicked from Helios, Wrye and COM.        If ( MenuMode == 1 )        Return    Else        If ( aaScheduleDay != 4 )            If ( GameHour > 23 )                StartScript "aa_PE_schedule1"                StartScript "aa_PE_schedule2"                StartScript "aa_PE_schedule3"            ElseIf ( GameHour < 5 )                StartScript "aa_PE_schedule1"                StartScript "aa_PE_schedule2"                StartScript "aa_PE_schedule3"            ...etc...            EndIf            Set aaScheduleDay To 4        Endif    EndIf    End

If there are too many instructions it might not be possible to run them all under if ( aaScheduleDay != 4 ).
User avatar
abi
 
Posts: 3405
Joined: Sat Nov 11, 2006 7:17 am

Post » Wed Dec 09, 2009 10:23 pm

...


Thank you for your advice :)

I'm using journal entries for the npcs that do have a quest associated with them but some of the staff are hired through dialogue.
Even then, maybe it would be better to create about 30 distinct quests with only one entry that would simple say "I have hired an alchemist"? and only use the global aaHiredNPC for those npcs that go on missions.

I use the schedule script only to move the npcs around, so it's all Positoncell. The npc that semi-appear are meant to be disabled. I forgot to say that they semi-appear even through walls, http://i656.photobucket.com/albums/uu289/Danae123123/blacknpc.jpg...

Using the aaSchedleControl global would mean that no npc moves around until they're all hired/found, which might be like, never.
Could I use a GetDiabled check in the schedule script so that only the npc that are enabled are affected? Or would that make things very messy?
User avatar
Tasha Clifford
 
Posts: 3295
Joined: Fri Jul 21, 2006 7:08 am

Post » Wed Dec 09, 2009 5:22 pm

Ah... I misinterpreted your situation. You described it well enough, but I got a different idea in my head - your screenshot was helpful. There are some adjustments to NPCs that cannot be made until they have a reference in the current game (you have to visit their cell). Applying PositionCell on them appears to be such a function.

I have performed a number of tests beginning with the situation you described. I entered Arrille's Tradehouse in Seyda Neen, disable Ajira (Balmora's Guild of Mages) though the console, moved her to my location using PositionCell and her black ghost appeared ready to crash my game when I activated her. Reloading the cell (leaving and entering Arrille's Tradehouse made the ghost disappear, but upon enabling her activation still crashed the game. Even my trick of a second round of disable/enable did not help in this situation. Any combination of actions where I did not first visit Ajira's starting cell (Balmora's Guild of Mages) resulted in a crash.

I believe that until the NPC's reference is created in the player's game its default position (as set in the editor) cannot be updated. Creating an image of the NPC using PositionCell results in a paradox for the engine and the subsequent crash. I have been highly successful with PositionCell after first visiting Ajira's cell. It does not appear to matter if she is enabled or disabled when the move is enacted.

I am little puzzled why your alchemist (and perhaps other staff of Pegasus Estate) do not begin in the position you are moving them. If it is a matter of speaking with them in advance to hire them then according to my tests you should not experience the problems you have (unless this is a result of the 72-hour effect - I had not tested what happens if such a delay occurs before repositioning the NPC). If meeting the NPC in advance is only a possibility and not a certainty, you might consider using a clone - one version to be met in the world and a second version to serve in Pegasus Estate. Then you need only disable and enable them as appropriate. I do not think there is a work-around for this requirement of meeting the NPC first.

As for using an if ( GetDisable == 1 ) check before enabling the NPCs in most situations it is an efficient check (no need to declare a local variable to monitor the NPC's state). However, the local variable is a small sacrifice and it is very reliable. When testing other modder's work (e.g. Ostar's Pax Redoran) I found problems with the if ( GetDisable == 1 ) check. In some instances the NPC could be disabled in a alternate quest path, but this line would allow the NPC to be re-enabled. Because it was difficult for me to anticipate when this would happen in another person's mod I just changed all instances to check a local variable (doOnce) to assure that if an NPC is disable he/she would stay disabled.

Edit: typo.
User avatar
Ella Loapaga
 
Posts: 3376
Joined: Fri Mar 09, 2007 2:45 pm

Post » Wed Dec 09, 2009 9:03 am

...

Thank you again. :icecream: here's what I'll do.
The initial trip to the estate is by boat but through dialogue/script. I'll make a new cell "Boat cabin" where all my npcs will be as their initial position with the schedule script off. The shipmaster will send me to the cabin (PositionCell by script for the PC) rather than the estate and the cabin door will lead to the Estate. The aaScheduleControl will be set to 1 at a later time.
I'll give that a try and let you know how that worked. I'll keep GetDisabled as plan B :)
User avatar
Philip Rua
 
Posts: 3348
Joined: Sun May 06, 2007 11:53 am

Post » Wed Dec 09, 2009 7:50 pm

Phew, I finally got the schedules to work the way I wanted, not using plan A or B, but pretty much plan G (rough estimation) which was my very ealy plan B.

Visiting the cell where all my scheduled npc are before they start moving around didn't do the trick (whether the npcs were enabled or diabled when I visited their inital cell, I wasn't sure I would matter so I tried both, just in case)

In the end, I put the schedule triggers in cells where there are no scheduled npcs at any time. I had to create a new 'Lobby' cell that the PC will have to visit often enough to have the schedule scripts run.

Anyhow, thanks for the help!
User avatar
Trista Jim
 
Posts: 3308
Joined: Sat Aug 25, 2007 10:39 pm


Return to III - Morrowind