Jump to content

Populating MCM AddMenuOptionST with player spells and lesser powers


Recommended Posts

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

  • Replies 54
  • Created
  • Last Reply

Top Posters In This Topic

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
EndEvent

Threw 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

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

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

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

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 by ReDragon2013
Link to comment
Share on other sites

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

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

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

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 variables

Be 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 by ReDragon2013
Link to comment
Share on other sites

  • Recently Browsing   0 members

    • No registered users viewing this page.

×
×
  • Create New...