ItsAlways710 Posted February 11, 2022 Author Share Posted February 11, 2022 Oh my, just looked over the CK Wiki Link, I am VERY excited to try this tonight!!!!! I will play with it and let you know - I ended up putting the version I posted last night into a 600+ mod load order with a level 69 player with A LOT of spells, it took about 41 seconds, so I have benchmarks in both large and small load orders now - thank you ReDragon2013!! I'll you you know how it goes :happy: -IA710 Link to comment Share on other sites More sharing options...
ItsAlways710 Posted February 11, 2022 Author Share Posted February 11, 2022 ReDragon2013 - I see why you are avoiding callbacks and futures, hoping 2 or 3 threads will do it without all the additional code futures and callbacks require, but I really see light at the end of this tunnel now! Sphered - I will look at putting it in OnPageReset - thank you for that. I am already using OnConfigOpen and Close, so I thought I could just bypass everything in OnConfigOpen with an IF if Spell Maintenance was running and force close the MCM, but I'll try it in OnPageReset instead - thank you! -IA710 Link to comment Share on other sites More sharing options...
ReDragon2013 Posted February 11, 2022 Share Posted February 11, 2022 (edited) I changed the code from version 6 and add to the same posting of mine version 7. Edited February 11, 2022 by ReDragon2013 Link to comment Share on other sites More sharing options...
ItsAlways710 Posted February 12, 2022 Author Share Posted February 12, 2022 The results are in and are quite puzzling.... 6a = 20 seconds7 = 18 seconds And I can't for the life of me figure out why..... I even started a new game for each version to verify.... Makes no sense to me... ? Link to comment Share on other sites More sharing options...
ItsAlways710 Posted February 12, 2022 Author Share Posted February 12, 2022 Yeah, I just recompiled version 7 and put it in the large load order and it took over 40 seconds, I can't seem to figure out why this isn't working.... I wish that there was a way to tell if a new mod was installed, that and if a player has removed a spell are the only reasons I am running this on game reload. Maybe I only run the big check in OnInit, and I figure a way on game reload to remove any spells that the player no longer has and run that as maintenance, totally relying on OnSpellLearned from that point? GetNthSpell showed promise, but doesn't account for race spells, which mods can change around, so I can't just find the race and add the vanilla spells and lesser powers manually.... Ugh!! Hmmmmm..... I may play with this idea some more... Link to comment Share on other sites More sharing options...
Sphered Posted February 12, 2022 Share Posted February 12, 2022 GetNthSpell is also a Race function Also you should be looking at using StringUtil. Split and SubString particularly. You can have one String encompass all the IDs of all spells for a mod for example. Not the point if that isnt wanted, but its a powerful tool we have there Link to comment Share on other sites More sharing options...
ReDragon2013 Posted February 12, 2022 Share Posted February 12, 2022 (edited) Like Sphered wrote anything to reduce the spell array entries and is fast in runtime would speed up your stringlist creation. You wrote: "And I can't for the life of me figure out why....."Not every script change is always the way in the right direction. As you know "Trial and Error" is the way to go. So I was thinking about my theory in version 6 and 7 and come to this. version 8 consists of 3 scriptsits70_PlayerAliasGlobal Scriptname its70_PlayerAliasGlobal Hidden {v8.0 ReD, 2022-02-12} ; this is our toolbox, a script without self ; -- FUNCTIONs -- 2 Function Update_HAC_SG(Actor player, Spell akSpell) Global If player.HasSpell(akSpell) Update_HAC(akSpell) EndIF EndFunction Function Update_HAC(Spell akSpell) Global string 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 its70_PlayerAliasScriptExtender Scriptname its70_PlayerAliasScriptExtender extends ReferenceAlias {v8.0 ReD, 2022-02-12} ; this is the main extender script our parent, "DO NOT ASSIGN to an ALIAS" !!! ;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 PROPERTY bRefresh auto Hidden ; [default=False] Spell[] PROPERTY a auto Hidden Function InitArray() a = PO3_SKSEFunctions.GetAllSpells() EndFunction Function DestroyArray() spell[] b a = b ; make array script variable unassigned EndFunction Int iFlag ; [default=0] Function UpdateFlag() ; helper iFlag = iFlag - 1 EndFunction ; -- Events -- ;====================================== 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 count of threads, that running now gotoState("Threading") ; ### STATE ### see child script RegisterForSingleUpdateGameTime(0.0) CreateSpellList() ; ** 1 ** build new storageutil stringlist UpdateFlag() 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 ;======= endState ; -- Functions -- 3 + 3 + (1) = 7 ;------------------------------- 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 ;---------------------------------------- Function Update_HAC_Spells(Spell akSpell) ; helper ;---------------------------------------- string 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 CreateSpellList() ;------------------------- actor player = Game.GetPlayer() int i = a.Length + 1 ; 100+1 If (i < 2) RETURN ; - STOP - empty array /or/ one element only EndIF While (i > 0) i = i - 2 ; a[99], a[97], .. ; 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() its70_PlayerAliasGlobal.Update_HAC_SG(player, a[i]) ; see "its70_PlayerAliasGlobal.psc", function EndIf EndWhile EndFunction its70_PlayerAliasScript Scriptname its70_PlayerAliasScript extends its70_PlayerAliasScriptExtender {v8.0 ReD, 2022-02-12} ; this is the script, which has to be assigned to the player alias ; ------------ ; -- Events -- 3 + "Threading" ; ------------ 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 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 UpdateFlag() ; see parent script "its70_PlayerAliasScriptExtender.psc" ENDEVENT ;======= endState ; -- FUNCTIONs -- (5) + 2 = 7 ;------------------ Function InitList() ;------------------ gotoState("Refresh") ; ### STATE ### see parent script "its70_PlayerAliasScriptExtender.psc", OnUpdate() RegisterForSingleUpdate(1.0) EndFunction ;------------------------------- Function CreateSpellList2(Int i) ;------------------------------- actor player = Game.GetPlayer() int i = a.Length ; 100 If (i == 0) RETURN ; - STOP - empty array EndIF If (i == 1) i = i + 1 ; adjust to get a[0] EndIf While (i > 0) i = i - 2 ; a[98], a[96], .. ; 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() its70_PlayerAliasGlobal.Update_HAC_SG(player, a[i]) ; see "its70_PlayerAliasGlobal.psc" EndIf EndWhile EndFunction Edited February 13, 2022 by ReDragon2013 Link to comment Share on other sites More sharing options...
ItsAlways710 Posted February 12, 2022 Author Share Posted February 12, 2022 I'll play with filtering the spell list later tonight when I have time. And I'll try not to get discouraged while experimenting :-) The one script gets assigned to the ref alias, I'm assuming I just attach the other two to the quest in CK? Link to comment Share on other sites More sharing options...
ReDragon2013 Posted February 13, 2022 Share Posted February 13, 2022 (edited) version 9 like Sphered gave us the hint. https://www.creationkit.com/index.php?title=GetNthSpell_-_Actor ;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") CreateSpellList() 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 ; -- Functions -- 4 Function InitList() ;-------- 1 gotoState("Refresh") ; ### STATE ### RegisterForSingleUpdate(1.0) EndFunction Function Update_HAC_Spells(Spell akSpell) ;-------- 2 string 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 InsertMenu_HAC_Spells() ;-------- 3 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 Function CreateSpellList() ;-------- 4 actor player = Game.GetPlayer() string s StorageUtil.StringlistClear(None, "HAC_Spells") ; *** clear *** ; https://www.creationkit.com/index.php?title=GetSpellCount_-_Actor ; https://www.creationkit.com/index.php?title=GetNthSpell_-_Actor int i = player.GetSpellCount() While (i) i = i - 1 spell akSpell = player.GetNthSpell(i) ; GetNthSpell(0) is the last added spell 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 EndWhile EndFunction About version 8: you wrote: "The one script gets assigned to the ref alias, I'm assuming I just attach the other two to the quest in CK?" Yes, its70_PlayerAliasScript has to be attached to PlayerAlias in your quest.No, the other two scripts put in as source files in the right papyrus directory. its70_PlayerAliasGlobal.psc its70_PlayerAliasScriptExtender.psc So the CK can find it for compiling. Normaly all three scripts should be compiled, if you compile the its70_PlayerAliasScript script with CK. I edited my post for version 8 as well! Edited February 13, 2022 by ReDragon2013 Link to comment Share on other sites More sharing options...
ItsAlways710 Posted February 13, 2022 Author Share Posted February 13, 2022 Yeah, that's why I was saying I would have to play with it, version 9 won't return race based spells or lesser powers (because GetNthSpell doesn't return them, it returns spells added to the player, race based spells are never added and unfortunately don't show up with this function - I compiled it and tested just to be sure, my modification is below, it just makes sure that you don't have things like spells you can't cast in the list). For a Dark Elf, in vanilla game, that means that the spell selection list would not list Ancestor's Wrath, Flames, Healing or Sparks when using just GetNthSpell(). Not such a big deal in the late game but a big deal in the early game. I was thinking maybe running the current big check in OnInit AND the GetNthSpell, compare them to find the "race based spells" that GetNthSpell will never return, store them, then in OnPlayerGameLoad we could just clear the stringlist add the "race based spells" that we saved earlier and then do the GetNthSpell like I modified it maybe? I'll have to play with it, I have a couple ideas on how to maybe do it, but need more time than I have at the moment LOL. There has to be a better way than this! I compiled v9 and in a game with under 20 spells it is instant, in a larger game with about 170 player spells it takes 6-7 seconds. Below is a snip of the function I changed in version 9: Function CreateSpellList() ;-------- 4 actor player = Game.GetPlayer() string s StorageUtil.StringlistClear(None, "HAC_Spells") ; *** clear *** ; https://www.creationkit.com/index.php?title=GetSpellCount_-_Actor ; https://www.creationkit.com/index.php?title=GetNthSpell_-_Actor int i = player.GetSpellCount() While (i) i = i - 1 spell akSpell = player.GetNthSpell(i) ; GetNthSpell(0) is the last added spell If akSpell.GetNthEffectMagicEffect(0).GetCastingType() 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 EndIf EndWhile EndFunction I have some things I need to do and if I have time I'll play with version 8 later tonight and let you know how that goes,,, Link to comment Share on other sites More sharing options...
Recommended Posts