ItsAlways710 Posted February 13, 2022 Author Share Posted February 13, 2022 Played with v8 last night, could never get it to compile, will play with it more tonight.. Link to comment Share on other sites More sharing options...
ItsAlways710 Posted February 14, 2022 Author Share Posted February 14, 2022 I was finally able to get v8 working, but it takes 21 seconds. Thinking about version 9 and doing something to capture those odd spells in OnInit and then just insert them right after we clear the stringlist in OnPlayerLoadGame. It would be another stringlist, but I would only be capturing it once.... Link to comment Share on other sites More sharing options...
ReDragon2013 Posted February 14, 2022 Share Posted February 14, 2022 (edited) I finally go through the papyrus source code and I was really astonished about this construct Function Update_HAC_Spells(Spell akSpell) 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(1) Next code line makes no sense, should be removed! StorageUtil.SetFormValue(None, s, akSpell as Form) ; Make sure that the spell name string is mapped to the correct formid(2) We know its not possible to have a duplicated spell by creation process, so we take the default value StorageUtil.StringListAdd(None, "HAC_Spells", s) ; Add a new spell name to stringlist with allowdupes == TRUEversion 10 with threading ;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] Int iFlag ; [default=0] Function UpdateFlag() ; helper ;------- iFlag = iFlag - 1 EndFunction ; -- Events -- 3 + "Refresh" Event OnInit() ; called first time mod will be seen on game (newgame or savegame) InitList() ; "first come, first serve" EndEvent Event OnPlayerLoadGame() ; called every time a mod was loaded by savegame (player alias script only!) InitList() ; "first come, first serve" EndEvent Event OnSpellLearned(Spell akSpell) ; added by Papyrus Extender SSE While ( bRefresh ) Utility.Wait(0.1) ; do the stringlist update for one thread only, any other (sneaking in) has to wait here 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, False) ; 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 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") StorageUtil.StringlistClear(None, "HAC_Spells") ; *** clear *** ; ------ iFlag = 3 ; prepare for count of threads, that running now gotoState("Threading") ; ### STATE ### RegisterForSingleUpdate(0.0) RegisterForSingleUpdateGameTime(0.0) CreateSpellList() ; thread *** 1 *** WHILE (iFlag) ; iFlag != 0 Utility.Wait(0.1) ; wait until all threads have finished the loop ENDWHILE ; ------ 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 EndFunction Event OnUpdate() Utility.Wait(0.1) CreateSpellList2() ; thread *** 2 *** Endevent Event OnUpdateGameTime() Utility.Wait(0.2) CreateSpellListByRace() ; thread *** 3 *** EndEvent ;======= endState ; -- Functions -- (1) + 6 Function InitList() ;-------- 1 gotoState("Refresh") ; ### STATE ### RegisterForSingleUpdate(1.0) EndFunction Function Update_HAC_Spells(Spell akSpell, Bool b=TRUE) ;-------- 2 ; None = save list globally ; "HAC_Spells" = "keyname" to know where to store ; akSpell.GetName() = string to save to "keyname" list ; b = allow dupes (default=TRUE) ; Add a new spell name to stringlist with allowdupes == TRUE StorageUtil.StringListAdd(None, "HAC_Spells", akSpell.GetName(), b) EndFunction Function InsertMenu_HAC_Spells() ;-------- 3 ; Sort the stringlist before insert the menu entry! StorageUtil.StringListSort(None, "HAC_Spells") ; 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() ; OnUpdate() "Refresh" ;-------- 4 actor player = Game.GetPlayer() ; https://www.creationkit.com/index.php?title=GetSpellCount_-_Actor ; https://www.creationkit.com/index.php?title=GetNthSpell_-_Actor ; i = 0 -> n/a ; i = 1 -> n/a ; i = 2 -> 2 - 2 --> GetNthSpell(0) ; i = 3 -> 3 - 2 --> GetNthSpell(1) ; i = 50 -> 50 - 2 --> GetNthSpell(48) int i = player.GetSpellCount() While (i > 1) i = i - 2 ; -2 ;;; ; return the base magicka cost of the spell ;;; int Function GetMagickaCost() native spell akSpell = player.GetNthSpell(i) If akSpell.GetNthEffectMagicEffect(0).GetCastingType() ; (0..2) ; 1 = Fire and Forget ; 2 = Concentration Update_HAC_Spells( akSpell ) EndIf EndWhile UpdateFlag() EndFunction Function CreateSpellList2() ; OnUpdate() "Threading" ;-------- 5 actor player = Game.GetPlayer() ; https://www.creationkit.com/index.php?title=GetSpellCount_-_Actor ; https://www.creationkit.com/index.php?title=GetNthSpell_-_Actor ; i = 0 -> n/a ; i = 1 -> 2 - 2 --> GetNthSpell(0) ; i = 2 -> 3 - 2 --> GetNthSpell(1) ; i = 3 -> 4 - 2 --> GetNthSpell(2) ; i = 50 -> 51 - 2 --> GetNthSpell(49) int i = player.GetSpellCount() If (i > 0) i = i + 1 ; after validation of spellcount +1 here EndIF While (i > 1) i = i - 2 ; -2 spell akSpell = player.GetNthSpell(i) If akSpell.GetNthEffectMagicEffect(0).GetCastingType() ; (0..2) ; 1 = Fire and Forget ; 2 = Concentration Update_HAC_Spells( akSpell ) EndIf EndWhile UpdateFlag() EndFunction FormList PROPERTY RaceSpecificSpells auto ; *** you have to create a new formlist by CK and ; fill with all spells which are not affected by SKSE function "player.GetNthSpell(i)" ** Function CreateSpellListByRace() ; OnUpdateGameTime() "Threading" ;-------- 6 actor player = Game.GetPlayer() int i = RaceSpecificSpells.GetSize() While (i) i = i - 1 ; -1 spell akSpell = RaceSpecificSpells.GetAt(i) as Spell If (akSpell) && player.HasSpell(akSpell) Update_HAC_Spells( akSpell ) EndIf EndWhile UpdateFlag() EndFunction For race specific spells, you have to create a formlist which has the vanilla spells. If DLCs or other mods should have such spells you can look for mod loaded by GeFormFromFile() and add such spell to list on the fly by using this https://www.creationkit.com/index.php?title=AddForm_-_FormList Sometimes it makes a different in runtime to use Utility.Wait() in functions and/or events. (3) maybe its faster to use this SKSE native spell function instead to get the MagicEffect of spell and check for ME.castingtype() ; return the base magicka cost of the spell int Function GetMagickaCost() native Edited February 14, 2022 by ReDragon2013 Link to comment Share on other sites More sharing options...
ItsAlways710 Posted February 14, 2022 Author Share Posted February 14, 2022 ReDragon2013, Your comment #1 - this is what is used to retrieve the FormID of the Spell for the MCM, it can not be removed, it is what makes this whole thing work, it maps the String to the FormID, this way we can sort the spell names and still get the FormID associated with that name.... We can't remove this unless we have another way to get the FormID of the spell from the string we are storing in the stringlist in the MCM..... Your comment #2 - You are correct, we most likely don't need the FALSE anymore as the creation process ensures no duplicates and we are clearing it first. This will speed it up some I am sure, good catch - thnak you! I think I added the FALSE in there when I was working on just updating the stringlist. We will need #1 in there, or some other way to get to the formID from the stringlist for this to be usable in the MCM.... Link to comment Share on other sites More sharing options...
Sphered Posted February 14, 2022 Share Posted February 14, 2022 StringUtil.psc Link to comment Share on other sites More sharing options...
ItsAlways710 Posted February 14, 2022 Author Share Posted February 14, 2022 I think Sphered mentioned earlier that everyone would be able to assist better if I clearly defined what I am trying to do with this, so I thought I would take a moment on lunch to refresh the goals for anyone trying to help: 1) I have a mod that requires the player to select a spell that they can cast in the MCM menu, I am using AddMenuOptionST in the MCM to do this, and therefore need a string array that I can pass to it.2) The string array should include all spells that the player can cast, and I would like it to be sorted alphabetically as in the late game you may have 200-300 spells that you can cast, them being sorted allows you to find the spell you want quicker.3) I need to be able to, once a spell name has been selected from the MCM Menu we populated earlier, get the FormID of the spell associated with the string I just selected so my mod can use it. 4) I have another mod that I am working on that would benefit from this as well, it would be NICE but not NECESSARY if I could share the same list with the other mod (I would have it all set to run the maintenance from one mod or the other, not both). StorageUtil allows for this. If there is a better way than what I have been trying I am certainly open to it. My current method is: 1) I need the stringlist for AddMenuOptionST so I HAVE to build it. 2) I'm going to sort the stringlist, so there is no reason to build a form list that matches the string list because the stringlist indexes are going to change when I sort it,3) so INSTEAD I am mapping the String that I am adding to the stringlist to a formID using StorageUtil.SetFormValue(None, s, akSpell as Form)4) then I can sort the stringlist and retrieve the formID with the string in the MCM once a selection has been made using StorageUtil.GetFormValue(None, s). I hope this clarifies what I am trying to do. If there is a better way I am definitely open to it.... GetNthSpell doesn't get all the spells, so I widened what I was getting to include ALL spells and then check if the player has the spell, but this is very slow (if numbers help, after we filter for GetCastType the list becomes about 1,285 spells in the load order I am doing most of the testing on). So that's why I'm looking at somehow coming up with the spells that GetNthSpell will never find and then use it, because it will only ever be, at most, all the spells that have been added to the player. I hope this helps! Link to comment Share on other sites More sharing options...
ItsAlways710 Posted February 14, 2022 Author Share Posted February 14, 2022 (edited) Edit: Double Post, removed. Edited February 14, 2022 by ItsAlways710 Link to comment Share on other sites More sharing options...
Sphered Posted February 14, 2022 Share Posted February 14, 2022 String Theory = 0x9DB72 + "ZZ" + 0x9FA64 + "ZZ" + 0xA0E7D + "ZZ" + 0xA59AD + "ZZ" + 0xA59AE + "ZZ" + 0xA0E7F + "ZZ" + 0xF5E0B + "ZZ" + 0x109AC2 + "ZZ" + 0x109AC3 GotoState(Theory) The ZZs is a way to isolate the indiv hexes. Anyway... Later.... Hey whats that long ass string I need those hexes now String Cheese = GetState() String[] Splut = StringUtil.Split(Cheese,"ZZ") Form ShouldBeHealSpellArt = Game.GetFormEx(Splut[2] as Int) You basically create an array using simply your state. You can declare the string instead up top etc. That single string can be read by your MCM You can append this too. Fetch it... add more. Remove stuff. Whatever. It can go beyond 128 items too. Just need a new separator so we can tell where 128 is and do a new split Just depends how dedicated you are to this. Sounds like you are, so... I suggest def getting acquainted with StringUtil Link to comment Share on other sites More sharing options...
ItsAlways710 Posted February 14, 2022 Author Share Posted February 14, 2022 Thank you Sphered, I'll take a longer look at StingUtil.psc, although I see you storing the FormIDs AS strings, I'm not sure how they are associated with THE string. In your example above, I see you creating an array of formIDs with "ZZ" as a delimiter in one string (Theory), then , then returning the formIDs as strings in a string list, splitting them on the delimiter "ZZ", but I'm not sure why I would want to do that for my use case? I'm trying to create a string array of player spell names, and then be able to grab the FormID of that spell later (knowing that I will sort the string array, so having two arrays, one string one form, doesn't work unless I can keep their indexes in sync as I sort the string list, which I have no idea how to do THAT other than something like StorageUtil or possibly JMap or JDB from JContainers which all allow you to map a form value to a string) and I'm trying to apply that to the example above and am quite confused.... And you are correct, I am VERY committed to this as I am learning A LOT from you all, even if it never ends up outside of my load order, every mod I ever make is going to benefit from what you folks are teaching me and I appreciate the time you all spend explaining things to me more than I can express... Thank you! Link to comment Share on other sites More sharing options...
ItsAlways710 Posted February 14, 2022 Author Share Posted February 14, 2022 And I'm a SQL guy, so that is making this all that much more difficult for me, because in SQL I would just create a table that has a String column and a FormID column, fill each, sort the whole thing on the string and return just the sorted string list when I need it, then lookup the FormID using String when I need it... Maybe I'm just stuck in 35 years of SQL thinking and can't get past the methods I've used for so long LOL, but it seems like this should be something easy and it shouldn't be something that I'm the first person to come up against... I was, at one point, creating a form array manually in the script with all the vanilla spells using GetFormFromFile(). I could, as ReDragon2013 suggested, just make a form list for that and run through it to add the vanilla spells/lesser powers, but then what if the end user is using a mod that changes those? My goal was to do it all via script and to account for everything that I could, but maybe I only deal with "Vanilla" race spells, and if they use a mod that changes that stuff they won't have access to those spells at all.... Link to comment Share on other sites More sharing options...
Recommended Posts