Jump to content
⚠ Known Issue: Media on User Profiles ×

Populating MCM AddMenuOptionST with player spells and lesser powers


Recommended Posts

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

  • Replies 54
  • Created
  • Last Reply

Top Posters In This Topic

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

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

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

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 scripts

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

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

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

  • Recently Browsing   0 members

    • No registered users viewing this page.

×
×
  • Create New...