ItsAlways710 Posted February 9, 2022 Author Share Posted February 9, 2022 Thank you Sphered and ReDragon2013. I typically use the += or -=, but in threads like this they often get broken down for readability, I was wondering if there was any difference between the two, good to know that readability is the only difference. ReDragon2013 - thank you for version 3 - I had inserted some STATEs in at one point and then pulled them out when they didn't fix what was wrong, but intended to add states or a bool in an IF like you did before I was done with the script - is one method better than the other? I would assume that the bool IF statement is quicker, but I don't really know a bunch about the speed of using STATEs. Also, thank you for reminding me to unregisterforSpellLearned, forgot about that!!!! Version 4 for Studying - I actually had the script something like this at some point, it was all FORMLIST based and I built the strings last (in fact when I was first writing this I started out using a CK FORMLIST, but wanted to sort and POSSIBLY use these lists in another mod, so PapyrusUTIL and JContainers were tested with PapyrusUTIL being the fastest of the methods I tested). In testing I found that the code I had for using the STRINGLIST ran a bit quicker for some reason. I would think that adding a FORM would be quicker than adding a (possibly) large string, however it didn't play out like that in my test system. I was thinking I could just not use a FORMLIST at all and use the STRINGLIST (this would be starting with verison 3) and the String:FormID MAP we create ONLY. Surely that would speed some things up wouldn't it? In the MCM when selecting a spell from the menu I can just do a GetFormValue instead of FormListGet with the index and totally avoid another list from being generated and maintained, right? Also in version 4, because we are building the FORMLIST, then building the STRINGLIST from it, then sorting the string list we won't be able to find the correct form in the FORMLIST will we? For instance, OnUpdate(): Formlist generates in CreateFormList FORM2[0]FORM3[1]FORM1[2] stringlist generates in FormsToStrings (indexes MATCH) FORM2_String[0]FORM3_String[1]FORM1_String[2] StringlistSort - now the stringlist gets sorted, but we do nothing with the formlist so now indexes DO NOT match FORM1_String[0]FORM2_String[1]FORM3_String[2] and Insert the "BACK" at index 0 (indexes are now totally off between the two lists, in Version 3 they would be offset by 1 but match) Version 4 output Strings FormIDs BACK[0]FORM1_String[1] = FORM2[0]FORM2_String[2] = FORM3[1]FORM3_String[3] = FORM1[2] Version 3 output Strings FormIDs BACK[0]FORM1_String[1] = FORM1[0]FORM2_String[2] = FORM2[1]FORM3_String[3] = FORM3[2] I am most likely missing something, but now how do I call the form when I select the string in the menu in the MCM? I don't think I would even be able to use the FORMLIST, I would have to use the MAP that we are still creating. So then why do the formlist at all (in either version, 3 or 4) because I am already storing the FORM tied to the STRING in the MAP? Thank you all for all the explanations, code suggestions and examples, very much appreciated! -IA710 Link to comment Share on other sites More sharing options...
Sphered Posted February 9, 2022 Share Posted February 9, 2022 Event OnPageReset(String Page) SetCursorFillMode(2) AddMenuOptionST("ListHirelings","Administer Hirelings","Choose") EndEvent Event OnMenuOpenST() If GetState() == "ListHirelings" Formlist Them = Whatever as Formlist String[] These = New String[7] These[0] = "No Selection" These[1] = Them.GetAt(0).GetDisplayName(); "Belrand" These[2] = Them.GetAt(1).GetDisplayName(); "Erik" These[3] = Them.GetAt(2).GetDisplayName(); "Jenassa" These[4] = Them.GetAt(3).GetDisplayName(); "Marcurio" These[5] = Them.GetAt(4).GetDisplayName(); "Stenvar" These[6] = Them.GetAt(5).GetDisplayName(); "Vorstag" SetMenuDialogStartIndex(0) SetMenuDialogDefaultIndex(0) SetMenuDialogOptions(These) EndIf EndEvent Event OnMenuAcceptST(Int ChosenDigit) Bool HandleIt = ChosenDigit > 0 && GetState() == "ListHirelings" If HandleIt ResolveFollower(FollowerList.GetAt(ChosenDigit - 1)) EndIf EndEventThrew this together and didnt do an array as to show one way listings can be done The key here, is 0 in this case, is cancel. The event is sent to say hey they clicked 0. So if 0 you do nothing, but if you want listing 0 in the formlist, you will have 1. So here, you fetch GetAt(ChosenOption Minus 1) As for the String vs Form arrays, I suppose its possible they query differently and give non-matched outputs. I am not familar with that approach so cant advise there but I wouldnt dismiss the possibility unless I looked at it Link to comment Share on other sites More sharing options...
ItsAlways710 Posted February 9, 2022 Author Share Posted February 9, 2022 Ahhhh, ok, I see what you are getting at Sphered, I will keep this in mind. I suppose I could also just insert a formID of None in index 0 of the formlist and have them completely match and not have to account for the offset at all. I had an outage in my datacenter last night and didn't get to do anything with this, but hopefully will have a few hours tonight. I will try some of the suggestions from above from you and ReDragon2013, and maybe try to just get it to a stringlist and a string:formID Map. I almost want to switch back to JContainers and try it again as I have a much better understanding of how to structure everything properly now thanks to this thread and everyone one in it LOL - thank you all for ALL the input! -IA710 Link to comment Share on other sites More sharing options...
ItsAlways710 Posted February 9, 2022 Author Share Posted February 9, 2022 Oh, and another question, it would be good practice to keep the player out of the MCM for the mod until the OnUpdate event has finished in the ReferenceAlias script (it takes about 23 seconds to run on a modest load order with very few player spells right now, I can see that being a good minute + on a very large load order). Any examples of a good way to accomplish that? I've searched and can't seem to find anything on it... Link to comment Share on other sites More sharing options...
ItsAlways710 Posted February 10, 2022 Author Share Posted February 10, 2022 I've had just a bit to play with it tonight, implemented some suggestions, made some changes. Totally dropped the formlist, it really is redundant, I didn't need to do much to the MCM script to do away with it, and it's really cleaner in the end. Here is were the ReferenceAlias script is right now. The OnUpdate is 20% faster now and I'm sure that the OnSpellLearned is quicker too - as usual feel free to point out any mistakes, I am sure there are some as usual :laugh: ;Import PO3_Events_Alias ;Import PO3_SKSEFunctions ;Import StorageUtil ; StorageUtil name: "HAC_Spells" ; list of strings ; StorageUtil "map": String as keyname to formID as value ; Return in MCM with StorageUtil.GetFormValue(None, (StorageUtil.StringListGet(None, "HAC_Spells", INDEX+ 1))) Bool bRefresh ; [default=False] ; -- Events -- 4 Event OnInit() ; called first time mod will be seen on game (newgame or savegame) RegisterForSingleUpdate(1.0) EndEvent Event OnPlayerLoadGame() ; called every time a mod was loaded by savegame (player alias script only!) RegisterForSingleUpdate(1.0) EndEvent Event OnUpdate() If ( bRefresh ) RETURN ; - STOP - Do not allow multiple threads at same time! EndIf bRefresh = TRUE ; *T* PO3_Events_Alias.UnRegisterForSpellLearned(self) Debug.Notification("HAC: Starting Spell Maintenance") CreateSpellList() ; Clear storageutil stringlist and add new spell strings Sort_Strings() ; Sort the final output player spell names stringlist. Debug.Notification("HAC: Spell Maintenance Completed") PO3_Events_Alias.RegisterForSpellLearned(self) ; register for special event bRefresh = False ; *** EndEvent Event OnSpellLearned(Spell akSpell) ; event added by Papyrus Script Extender While (bRefresh) Utility.Wait(0.1) ; wait here until main refresh is finished EndWhile Update_HAC_Spells(akSpell) ; add the spell to storageutil stringlist Sort_Strings() ; Sort the final output player spell names stringlist. EndEvent ; -- Functions -- 3 Function Update_HAC_Spells(Spell akSpell) ; helper string s = akSpell.GetName() StorageUtil.SetFormValue(None, s, akSpell as Form) ; Make sure that the spell name string is mapped to the correct formid StorageUtil.StringListAdd(None, "HAC_Spells", s, False) ; Add a new spell name to stringlist with allowdupes == FALSE EndFunction Function CreateSpellList() ; helper actor player = Game.GetPlayer() spell[] a = PO3_SKSEFunctions.GetAllSpells() ; Build a temp array with all spells in the game StorageUtil.StringlistClear(None, "HAC_Spells") ; *** clear *** strings int i = a.Length While (i) ; (i != 0) i = i - 1 spell akSpell = a[i] ; get the first MagicEffect of valid spell and give back the casting type (0..2) If (akSpell) && akSpell.GetNthEffectMagicEffect(0).GetCastingType() ; 1 = Fire and Forget, 2 = Concentration If player.HasSpell(akSpell) ; check the playable spell from array to see if the player has it Update_HAC_Spells(akSpell) EndIf EndIf EndWhile EndFunction Function Sort_Strings() ; helper function StorageUtil.StringListSort(None, "HAC_Spells") ; Sort the final output player spell names stringlist. ; Make Absolutely sure that Back isn't in the list StorageUtil.StringListRemove(None, "HAC_Spells", "Back / Cancel / No Change", True) ; insert at index 0 of sorted stringlist an escape menu as way out StorageUtil.StringListInsert(None, "HAC_Spells", 0, "Back / Cancel / No Change") EndFunction Once again, thank you all! Link to comment Share on other sites More sharing options...
ReDragon2013 Posted February 10, 2022 Share Posted February 10, 2022 (edited) suggestion version 5 ;Import PO3_Events_Alias ;Import PO3_SKSEFunctions ; https://github.com/powerof3/PapyrusExtenderSSE/tree/master/src/Papyrus ;Import StorageUtil ; https://github.com/Vicyntae/SCLSE/blob/master/Data/Source/Scripts/StorageUtil.psc ; StorageUtil name: "HAC_Spells" ; list of strings, names of spells the player has currently ; StorageUtil name: "map" ; list of strings as keyname to formID as value ; used in MCM script with StorageUtil.GetFormValue(None, (StorageUtil.StringListGet(None, "HAC_Spells", INDEX+1))) Bool bRefresh ; [default=False] ; ------------ ; -- Events -- 4 ; ------------ Event OnInit() ; called first time mod will be seen on game (newgame or savegame) RegisterForSingleUpdate(1.0) EndEvent Event OnPlayerLoadGame() ; called every time a mod was loaded by savegame (player alias script only!) RegisterForSingleUpdate(1.0) EndEvent Event OnUpdate() If ( bRefresh ) RETURN ; - STOP - Do not allow multiple threads at same time! EndIf ; ------------------------ bRefresh = TRUE ; *T* PO3_Events_Alias.UnRegisterForSpellLearned(self) ; unregister for the special event Debug.Notification("HAC: Starting Spell Maintenance") CreateSpellList() ; Clear storageutil stringlist and build it new StorageUtil.StringListSort(None, "HAC_Spells") ; Sort the stringlist. InsertMenu_HAC_Spells() ; Insert the escape menu Debug.Notification("HAC: Spell Maintenance Completed") PO3_Events_Alias.RegisterForSpellLearned(self) ; register for special event bRefresh = False ; *** EndEvent Event OnSpellLearned(Spell akSpell) ; added by Papyrus Extender SSE WHILE ( bRefresh ) Utility.Wait(0.1) ; just in case, do not allow another thread to sneak in to an update in progress ENDWHILE bRefresh = TRUE ; *T* StorageUtil.StringListRemoveAt(None, "HAC_Spells", 0) ; remove the escape menu, before adding a new spell to stringlist Update_HAC_Spells(akSpell) ; add this spell to stringlist StorageUtil.StringListSort(None, "HAC_Spells") ; sort the stringlist (of final output player known spell names) InsertMenu_HAC_Spells() ; insert the escape menu bRefresh = False ; *** EndEvent ; --------------- ; -- Functions -- 3 ; --------------- ; use a script variable instead of function variable to avoid initial memory cost each time of calling next function String s Function Update_HAC_Spells(Spell akSpell) ; helper ; get the first effect of the spell and give back the casting type (0..2) ; 0 = constant effect (not desired to add into stringlist) ; 1 = Fire and Forget ; 2 = Concentration If akSpell.GetNthEffectMagicEffect(0).GetCastingType() s = akSpell.GetName() ; get name of spell StorageUtil.SetFormValue(None, s, akSpell as Form) ; Make sure that the spell name string is mapped to the correct formid StorageUtil.StringListAdd(None, "HAC_Spells", s, False) ; Add a new spell name to stringlist with allowdupes == FALSE Endif EndFunction Function InsertMenu_HAC_Spells() ; helper ; insert at index 0 of sorted stringlist an escape menu as way out StorageUtil.StringListInsert(None, "HAC_Spells", 0, "Back / Cancel / No Change") EndFunction Function CreateSpellList() actor player = self.GetActorReference() ; for this kind of script its the same as "actor player = Game.GetPlayer()" spell[] a = PO3_SKSEFunctions.GetAllSpells() ; Build a temp array with all spells in the game StorageUtil.StringlistClear(None, "HAC_Spells") ; *** clear *** strings int i = a.Length While (i) ; (i != 0) i = i - 1 If player.HasSpell( a[i] ) ; check spell from array to see if the player knows it or not Update_HAC_Spells( a[i] ) EndIf EndWhile EndFunction What is new?- OnSpellLearned() got own thread handling- You should try, if its useful to make "string s" as script variable instead of function variable!- The conditions in main loop has been changed. Maybe useful for speed up! Edited February 10, 2022 by ReDragon2013 Link to comment Share on other sites More sharing options...
ItsAlways710 Posted February 10, 2022 Author Share Posted February 10, 2022 Thank you ReDragon2013, I'll give this a shot in it's entirety this evening. I see how you simplified the check in the main loop, here's hoping that helps - it looks like it will :-) I may put this in a larger load order and see how long it takes there tonight as well. I think I have a save in it where I have over 200 player spells (I have Forget Spell installed, but evidently I'm a hoarder in Skyrim LOL). I will post what I find before I go to sleep - and thank you all again! -IA710 Link to comment Share on other sites More sharing options...
ItsAlways710 Posted February 11, 2022 Author Share Posted February 11, 2022 ReDragon2013, I tried the script as was and had to change self.GetActorReference() back to Game.GetPlayer() for it to load any spells. Not sure why.... I think that the last version ran in 24 second, the fastest any version has run has been 21 seconds, this version ran 30,,, I was looking through it and remembered that early on when I was breaking things apart and inserting debug messages in it the player.HasSpell() was the costliest thing that I was doing repeatedly, so I moved the akSpell.GetNthEffectMagicEffect(0).GetCastingType() IF back right before the HasSpell IF and BANG, it ran in 21 seconds, a 30% decrease!... I thought that there might be something left, and ended up with this running in 19 Seconds!!!!! ;Import PO3_Events_Alias ;Import PO3_SKSEFunctions ; https://github.com/powerof3/PapyrusExtenderSSE/tree/master/src/Papyrus ;Import StorageUtil ; https://github.com/Vicyntae/SCLSE/blob/master/Data/Source/Scripts/StorageUtil.psc ; StorageUtil name: "HAC_Spells" ; list of strings, names of spells the player has currently ; StorageUtil name: "map" ; list of strings as keyname to formID as value ; used in MCM script with StorageUtil.GetFormValue(None, (StorageUtil.StringListGet(None, "HAC_Spells", INDEX+1))) Bool bRefresh ; [default=False] ; ------------ ; -- Events -- 4 ; ------------ Event OnInit() ; called first time mod will be seen on game (newgame or savegame) RegisterForSingleUpdate(1.0) EndEvent Event OnPlayerLoadGame() ; called every time a mod was loaded by savegame (player alias script only!) RegisterForSingleUpdate(1.0) EndEvent Event OnUpdate() If ( bRefresh ) RETURN ; - STOP - Do not allow multiple threads at same time! EndIf ; ------------------------ bRefresh = TRUE ; *T* PO3_Events_Alias.UnRegisterForSpellLearned(self) ; unregister for the special event Debug.Notification("HAC: Starting Spell Maintenance") CreateSpellList() ; Clear storageutil stringlist and build it new InsertMenu_HAC_Spells() ; Insert the escape menu Debug.Notification("HAC: Spell Maintenance Completed") PO3_Events_Alias.RegisterForSpellLearned(self) ; register for special event bRefresh = False ; *** EndEvent Event OnSpellLearned(Spell akSpell) ; added by Papyrus Extender SSE While ( bRefresh ) Utility.Wait(0.1) ; just in case, do not allow another thread to sneak in to an update in progress EndWhile bRefresh = TRUE ; *T* StorageUtil.StringListRemoveAt(None, "HAC_Spells", 0) ; remove the escape menu, before adding a new spell to stringlist Update_HAC_Spells(akSpell) ; add this spell to stringlist InsertMenu_HAC_Spells() ; insert the escape menu bRefresh = False ; *** EndEvent ; --------------- ; -- Functions -- 3 ; --------------- ; use a script variable instead of function variable to avoid initial memory cost each time of calling next function String s Function Update_HAC_Spells(Spell akSpell) ; helper s = akSpell.GetName() ; get name of spell StorageUtil.SetFormValue(None, s, akSpell as Form) ; Make sure that the spell name string is mapped to the correct formid StorageUtil.StringListAdd(None, "HAC_Spells", s, False) ; Add a new spell name to stringlist with allowdupes == FALSE EndFunction Function InsertMenu_HAC_Spells() ; helper StorageUtil.StringListSort(None, "HAC_Spells") ; Sort the stringlist. ; insert at index 0 of sorted stringlist an escape menu as way out StorageUtil.StringListInsert(None, "HAC_Spells", 0, "Back / Cancel / No Change") EndFunction Function CreateSpellList() actor player = Game.GetPlayer() ; for this kind of script its the same as "actor player = Game.GetPlayer()" spell[] a = PO3_SKSEFunctions.GetAllSpells() ; Build a temp array with all spells in the game StorageUtil.StringlistClear(None, "HAC_Spells") ; *** clear *** strings int i = a.Length While (i) ; (i != 0) i = i - 1 ; get the first effect of the spell and give back the casting type (0..2) ; 0 = constant effect (not desired to add into stringlist) ; 1 = Fire and Forget ; 2 = Concentration If a[i].GetNthEffectMagicEffect(0).GetCastingType() If player.HasSpell( a[i] ) ; check spell from array to see if the player knows it or not Update_HAC_Spells( a[i] ) EndIf EndIf EndWhile EndFunction Have you given any thought to a way to keep the MCM from opening while the OnUpdate event is running? I know I could set a global variable, or even a StorageUtil value and check for in in OnConfigInit and if it's set then prompt the user to wait until Spell Maintenance has completed and force close the MCM, just wondering if there is a better way? Thank you for all your help, it's getting faster and faster! -IA710 Link to comment Share on other sites More sharing options...
Sphered Posted February 11, 2022 Share Posted February 11, 2022 Not a fan of OnConfigOpen/OnConfigClose. I literally never use those OnPageReset is where you want your checker. Populate a MCM is busy so buzz off line or whatever until the update is done This said, I like to use a "commit changes?" type button where when clicked, closes menus and performs whatever requests. I suppose otherwise you would want a OnConfigClose event to know when to commence Link to comment Share on other sites More sharing options...
ReDragon2013 Posted February 11, 2022 Share Posted February 11, 2022 (edited) I do not have any experience with skyui event management. suggestion version 6a ;Import PO3_Events_Alias ;Import PO3_SKSEFunctions ; https://github.com/powerof3/PapyrusExtenderSSE/tree/master/src/Papyrus ;Import StorageUtil ; https://github.com/Vicyntae/SCLSE/blob/master/Data/Source/Scripts/StorageUtil.psc ; StorageUtil name: "HAC_Spells" ; list of strings, names of spells the player has currently ; StorageUtil name: "map" ; list of strings as keyname to formID as value ; used in MCM script with StorageUtil.GetFormValue(None, (StorageUtil.StringListGet(None, "HAC_Spells", INDEX+1))) Bool bRefresh ; [default=False] ; ------------ ; -- Events -- 3 + "Refresh" ; ------------ Event OnInit() ; called first time mod will be seen on game (newgame or savegame) InitList() EndEvent Event OnPlayerLoadGame() ; called every time a mod was loaded by savegame (player alias script only!) InitList() EndEvent Event OnSpellLearned(Spell akSpell) ; added by Papyrus Extender SSE While ( bRefresh ) Utility.Wait(0.1) ; just in case, do not allow another thread to sneak in to an update in progress EndWhile bRefresh = TRUE ; *T* StorageUtil.StringListRemoveAt(None, "HAC_Spells", 0) ; remove the escape menu, before adding a new spell to stringlist Update_HAC_Spells(akSpell) ; add this spell to stringlist InsertMenu_HAC_Spells() ; insert the escape menu bRefresh = False ; *** EndEvent ;====================================== State Refresh ;============ Function InitList() ; empty here, OnInit() or OnPlayerLoadGame() event can be the first ("first come, first serve") EndFunction Event OnUpdate() If ( bRefresh ) RETURN ; - STOP - Just in case! EndIf ; ------------------------ bRefresh = TRUE ; *T* PO3_Events_Alias.UnRegisterForSpellLearned(self) ; unregister for the special event Debug.Notification("HAC: Starting Spell Maintenance") InitArray() ; Build array with all spells ingame StorageUtil.StringlistClear(None, "HAC_Spells") ; *** clear *** iFlag = 2 ; prepare for two threads running now RegisterForSingleUpdateGameTime(0.0) CreateSpellList() ; ** 1 ** build new storageutil stringlist WHILE (iFlag) ; iFlag != 0 Utility.Wait(0.1) ; wait until both threads have finished the loop ENDWHILE DestroyArray() ; clean up the spell array InsertMenu_HAC_Spells() ; Insert the escape menu gotoState("") ; ### STATE ### Debug.Notification("HAC: Spell Maintenance Completed") PO3_Events_Alias.RegisterForSpellLearned(self) ; register for special event bRefresh = False ; *** EndEvent Event OnUpdateGameTime() CreateSpellList2() ; ** 2 ** build new storageutil stringlist EndEvent ;======= endState ; --------------- ; -- Functions -- 1 + 5 + 2 = 8 ; --------------- Function InitList() gotoState("Refresh") ; ### STATE ### RegisterForSingleUpdate(1.0) EndFunction ;------------------------------------------------------------------------------------------------------------------- Int iFlag Function UpdateFlag() ; helper iFlag = iFlag - 1 EndFunction Spell[] a Function InitArray() a = PO3_SKSEFunctions.GetAllSpells() EndFunction Function DestroyArray() spell[] b a = b ; make array variable "a" unassigned s = "" ; clear string s2 = "" ; clear string EndFunction Function CreateSpellList() ; OnUpdate() actor player = Game.GetPlayer() int i = a.Length + 1 ; 100 + 1 = 101 99 + 1 = 100 While (i > 0) i = i - 2 ; a[99], a[97], .., a[3], a[1] a[98], a[96], .., a[4], a[2], a[0] ; get the first effect of the spell and give back the casting type (0..2) ; 0 = constant effect (not desired to add into stringlist) ; 1 = Fire and Forget ; 2 = Concentration If a[i].GetNthEffectMagicEffect(0).GetCastingType() If player.HasSpell( a[i] ) ; check spell from array to see if the player knows or not Update_HAC_Spells( a[i] ) EndIf EndIf EndWhile UpdateFlag() EndFunction Function CreateSpellList2() ; OnUpdateGameTime() actor player = Game.GetPlayer() int i = a.Length ; 100 99 While (i > 0) i = i - 2 ; a[98], a[96], .., a[2], a[0] a[97], a[95], .., a[3], a[1] ; get the first effect of the spell and give back the casting type (0..2) ; 0 = constant effect (not desired to add into stringlist) ; 1 = Fire and Forget ; 2 = Concentration If a[i].GetNthEffectMagicEffect(0).GetCastingType() If player.HasSpell( a[i] ) ; check spell from array to see if the player knows or not Update_HAC_Spells2( a[i] ) EndIf EndIf EndWhile UpdateFlag() EndFunction ;------------------------------------------------------------------------------------------------------------------- ; use a script variable instead of function variable to avoid initial memory cost each time of calling next function String s String s2 Function Update_HAC_Spells(Spell akSpell) ; helper s = akSpell.GetName() ; get the spell name as string StorageUtil.SetFormValue(None, s, akSpell as Form) ; Make sure that the spell name string is mapped to the correct formid StorageUtil.StringListAdd(None, "HAC_Spells", s, False) ; Add a new spell name to stringlist with allowdupes == FALSE EndFunction Function Update_HAC_Spells2(Spell akSpell) ; helper s2 = akSpell.GetName() ; get the spell name as string StorageUtil.SetFormValue(None, s2, akSpell as Form) ; Make sure that the spell name string is mapped to the correct formid StorageUtil.StringListAdd(None, "HAC_Spells", s2, False) ; Add a new spell name to stringlist with allowdupes == FALSE EndFunction Function InsertMenu_HAC_Spells() ; helper StorageUtil.StringListSort(None, "HAC_Spells") ; Sort the stringlist before insert the menu entry! ; insert to stringlist at index 0 an escape menu as way out StorageUtil.StringListInsert(None, "HAC_Spells", 0, "Back / Cancel / No Change") EndFunction What is new?Try to get speed up by using two threads to create the final stringlist, required more conditions, more code and more script variablesBe warned it's an approach, not fully tested! In theory we could run three threads at time without using "futures or callbacks". https://www.creationkit.com/index.php?title=Creating_Multithreaded_Skyrim_Mods suggestion version 7 required some debugging to calculate the right counter conditions for the loops !! ;Import PO3_Events_Alias ;Import PO3_SKSEFunctions ; https://github.com/powerof3/PapyrusExtenderSSE/tree/master/src/Papyrus ;Import StorageUtil ; https://github.com/Vicyntae/SCLSE/blob/master/Data/Source/Scripts/StorageUtil.psc ; StorageUtil name: "HAC_Spells" ; list of strings, names of spells the player has currently ; StorageUtil name: "map" ; list of strings as keyname to formID as value ; used in MCM script with StorageUtil.GetFormValue(None, (StorageUtil.StringListGet(None, "HAC_Spells", INDEX+1))) Bool bRefresh ; [default=False] ; ------------ ; -- Events -- 3 + "Refresh" ; ------------ Event OnInit() ; called first time mod will be seen on game (newgame or savegame) InitList() EndEvent Event OnPlayerLoadGame() ; called every time a mod was loaded by savegame (player alias script only!) InitList() EndEvent Event OnSpellLearned(Spell akSpell) ; added by Papyrus Extender SSE While ( bRefresh ) Utility.Wait(0.1) ; just in case, do not allow another thread to sneak in to an update in progress EndWhile bRefresh = TRUE ; *T* StorageUtil.StringListRemoveAt(None, "HAC_Spells", 0) ; remove the escape menu, before adding a new spell to stringlist Update_HAC_Spells(akSpell) ; add this spell to stringlist InsertMenu_HAC_Spells() ; insert the escape menu bRefresh = False ; *** EndEvent ;====================================== State Refresh ;============ Function InitList() ; empty here, OnInit() or OnPlayerLoadGame() event can be the first ("first come, first serve") EndFunction Event OnUpdate() If ( bRefresh ) RETURN ; - STOP - Just in case! EndIf ; ------------------------ bRefresh = TRUE ; *T* PO3_Events_Alias.UnRegisterForSpellLearned(self) ; unregister for the special event Debug.Notification("HAC: Starting Spell Maintenance") InitArray() ; Build array with all spells ingame StorageUtil.StringlistClear(None, "HAC_Spells") ; *** clear *** iFlag = 3 ; prepare for three threads running now gotoState("Threading") ; ### STATE ### RegisterForSingleUpdateGameTime(0.0) RegisterForSingleUpdate(0.0) CreateSpellList() ; ** 1 ** build new storageutil stringlist WHILE (iFlag) ; iFlag != 0 Utility.Wait(0.1) ; wait until all threads have finished the loop ENDWHILE DestroyArray() ; clean up the spell array InsertMenu_HAC_Spells() ; Insert the escape menu gotoState("") ; ### STATE ### Debug.Notification("HAC: Spell Maintenance Completed") PO3_Events_Alias.RegisterForSpellLearned(self) ; register for special event bRefresh = False ; *** EndEvent ;======= endState ;====================================== State Threading ;============== Function InitList() ; empty here, OnInit() or OnPlayerLoadGame() event can be the first ("first come, first serve") EndFunction Event OnUpdateGameTime() CreateSpellList2() ; ** 2 ** build new storageutil stringlist EndEvent Event OnUpdate() CreateSpellList3() ; ** 3 ** build new storageutil stringlist EndEvent ;======= endState ; --------------- ; -- Functions -- 1 + 6 + 4 = 11 ; --------------- Function InitList() gotoState("Refresh") ; ### STATE ### RegisterForSingleUpdate(1.0) EndFunction ;------------------------------------------------------------------------------------------------------------------- Int iFlag Function UpdateFlag() ; helper iFlag = iFlag - 1 EndFunction Spell[] a Function InitArray() a = PO3_SKSEFunctions.GetAllSpells() EndFunction Function DestroyArray() spell[] b a = b ; make array variable "a" unassigned EndFunction Function CreateSpellList() ; first thread actor player = Game.GetPlayer() int i = (a.Length / 3) + 1 ; 100 -> a[33] .. a[0] While (i) i = i - 1 ; get the first effect of the spell and give back the casting type (0..2) ; 0 = constant effect (not desired to add into stringlist) ; 1 = Fire and Forget ; 2 = Concentration If a[i].GetNthEffectMagicEffect(0).GetCastingType() If player.HasSpell( a[i] ) ; check spell from array to see if the player knows or not Update_HAC_Spells( a[i] ) EndIf EndIf EndWhile UpdateFlag() EndFunction Function CreateSpellList2() ; 2nd thread - OnUpdateGameTime() actor player = Game.GetPlayer() int m = a.Length / 3 int i = a.Length - m + 1 ; 100 -> a[66] .. a[34] While (i > m) i = i - 1 ; get the first effect of the spell and give back the casting type (0..2) ; 0 = constant effect (not desired to add into stringlist) ; 1 = Fire and Forget ; 2 = Concentration If a[i].GetNthEffectMagicEffect(0).GetCastingType() If player.HasSpell( a[i] ) ; check spell from array to see if the player knows or not Update_HAC_Spells2( a[i] ) EndIf EndIf EndWhile UpdateFlag() EndFunction Function CreateSpellList3() ; 3rd thread - OnUpdate() actor player = Game.GetPlayer() int i = a.Length ; 100 -> a[99] .. a[67] int m = i - (i / 3) While (i > m) i = i - 1 ; get the first effect of the spell and give back the casting type (0..2) ; 0 = constant effect (not desired to add into stringlist) ; 1 = Fire and Forget ; 2 = Concentration If a[i].GetNthEffectMagicEffect(0).GetCastingType() If player.HasSpell( a[i] ) ; check spell from array to see if the player knows or not Update_HAC_Spells2( a[i] ) EndIf EndIf EndWhile UpdateFlag() EndFunction ;------------------------------------------------------------------------------------------------------------------- ; use a script variable instead of function variable to avoid initial memory cost each time of calling next function String s String s2 String s3 Function Update_HAC_Spells(Spell akSpell) ; helper s = akSpell.GetName() ; get the spell name as string StorageUtil.SetFormValue(None, s, akSpell as Form) ; Make sure that the spell name string is mapped to the correct formid StorageUtil.StringListAdd(None, "HAC_Spells", s, False) ; Add a new spell name to stringlist with allowdupes == FALSE EndFunction Function Update_HAC_Spells2(Spell akSpell) ; helper s2 = akSpell.GetName() ; get the spell name as string StorageUtil.SetFormValue(None, s2, akSpell as Form) ; Make sure that the spell name string is mapped to the correct formid StorageUtil.StringListAdd(None, "HAC_Spells", s2, False) ; Add a new spell name to stringlist with allowdupes == FALSE EndFunction Function Update_HAC_Spells3(Spell akSpell) ; helper s3 = akSpell.GetName() ; get the spell name as string StorageUtil.SetFormValue(None, s3, akSpell as Form) ; Make sure that the spell name string is mapped to the correct formid StorageUtil.StringListAdd(None, "HAC_Spells", s3, False) ; Add a new spell name to stringlist with allowdupes == FALSE EndFunction Function InsertMenu_HAC_Spells() ; helper ; clear each script variable s = "" s2 = "" s3 = "" StorageUtil.StringListSort(None, "HAC_Spells") ; Sort the stringlist before insert the menu entry! ; insert to stringlist at index 0 an escape menu as way out StorageUtil.StringListInsert(None, "HAC_Spells", 0, "Back / Cancel / No Change") EndFunction Edited February 11, 2022 by ReDragon2013 Link to comment Share on other sites More sharing options...
Recommended Posts