Jump to content

ReDragon2013

Members
  • Posts

    640
  • Joined

  • Last visited

Posts posted by ReDragon2013

  1. 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 == TRUE

    version 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
  2. 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!

     

     

  3. 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

     

     

  4. 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

     

     

  5. 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!

  6. as Sphered wrote:

    this code

     int i
     i = i + 1

    do the same as next code

     int i
     i += 1

    BUT, what is better to understand? Keep in mind there is no different in papyrus executable script. wiki link: https://www.creationkit.com/index.php?title=Statement_Reference

     

    version 3 for code optimization

     

    ;Import PO3_Events_Alias
    ;Import PO3_SKSEFunctions
    
    ;Import StorageUtil
    
        ; StorageUtil name: "HAC_Spells"         ; list of strings
        ; StorageUtil name: "HAC_SpForms"        ; list of forms
    ;-------------------------------------------------------------
    
      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()
        Debug.Notification("HAC: Starting Spell Maintenance")
    
        CreateSpellList()                                            ; Clear storageutil stringlist and add new spell strings
        StorageUtil.StringListSort(None, "HAC_Spells")               ; Sort the final output player spell names stringlist.
        Strings_To_Forms()                                           ; Clear storageutil list of spell forms and add new forms
    
        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
    
    ; remove existing escape menu from stringlist before sorting
        StorageUtil.StringListRemove(None, "HAC_Spells", "Back / Cancel / No Change", True)
    
        StorageUtil.StringListSort(None, "HAC_Spells")               ; Sort the stringlist of player spell names to output on screen.
        Strings_To_Forms()                                           ; Clear storageutil list of spell forms and add new forms
    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 Strings_To_Forms()  ; helper
    
        int m = StorageUtil.StringListCount(None, "HAC_Spells")      ; get length of stringlist
        StorageUtil.FormListClear(None, "HAC_SpForms")               ; *** clear *** forms
    
    ; use the sorted stringlist to build the store the same a formlist (without escape menu)
        int i = 0
        WHILE (i < m)
            form fm = StorageUtil.GetFormValue(None, StorageUtil.StringListGet(None, "HAC_Spells", i))
            IF ( fm )
                StorageUtil.FormListAdd(None, "HAC_SpForms", fm)
            ENDIF
            i = i + 1
        ENDWHILE
    
    ; insert at index 0 of stringlist the escape menu as way out
        StorageUtil.StringListInsert(None, "HAC_Spells", 0, "Back / Cancel / No Change")
    
    EndFunction

     

     

    version 4 for studying, it is creating the formlist first and the stringlist after

     

    ;Import PO3_Events_Alias
    ;Import PO3_SKSEFunctions
    ; https://www.nexusmods.com/skyrimspecialedition/mods/22854?tab=files&BH=0
    ; https://github.com/powerof3/PapyrusExtenderSSE/tree/master/src/Papyrus
    
    ;Import StorageUtil
    
        ; StorageUtil name: "HAC_Spells"         ; list of strings, will be sorted by script on every spell update
        ; StorageUtil name: "HAC_SpForms"        ; list of forms, keeps unsorted
    ;-------------------------------------------------------------------------------------------------------------
    
      Bool bRefresh        ; [default=False]
    
    
    ; -- EVENTs -- 4
    
    EVENT OnInit()                      ; called first time mod will be seen on game (newgame or savegame)
    ;=========== 1
        RegisterForSingleUpdate(1.0)
    ENDEVENT
    
    
    EVENT OnPlayerLoadGame()            ; called every time a mod was loaded by savegame (player alias script only!)
    ;===================== 2
        RegisterForSingleUpdate(1.0)
    ENDEVENT
    
    
    EVENT OnUpdate()
    ;============= 3
    IF ( bRefresh )
        RETURN    ; - STOP -    Do not allow multiple threads at same time!
    ENDIF
    ;---------------------
        bRefresh = TRUE        ; *T*
    
        PO3_Events_Alias.UnRegisterForSpellLearned(self)              ; unregister for special event
        Debug.Notification("HAC: Starting Spell Maintenance")         ; ### debug info ###
    
        CreateFormList()                                              ; Clear storageutil formlist and add all player spells
        Forms_To_Strings()                                            ; Clear storageutil stringlist and build it new
    
    ;---
    ; Sort the final output player spell names stringlist.
        StorageUtil.StringListSort(None, "HAC_Spells")
    
    ; insert at index 0 of stringlist the escape menu as way out
        StorageUtil.StringListInsert(None, "HAC_Spells", 0, "Back / Cancel / No Change")
    ;---
    
        Debug.Notification("HAC: Spell Maintenance Completed")        ; ### debug info ###
        PO3_Events_Alias.RegisterForSpellLearned(self)                ; register for special event
    
        bRefresh = False    ; ***
    ENDEVENT
    
    
    EVENT OnSpellLearned(Spell akSpell)  ; event added by powerofthrees Papyrus Extender
    ;=================== 4
    WHILE (bRefresh)
        Utility.Wait(0.1)    ; wait here until main refresh is finished, just in case
    ENDWHILE
    
        StorageUtil.FormListAdd(None, "HAC_SpForms", akSpell as Form, False)
    
        StorageUtil.StringListRemoveAt(None, "HAC_Spells", 0)         ; remove the escape menu from stringlist before adding a new spell
        Update_HAC_Spells(akSpell)                                    ; add this spell to storageutil stringlist
    
    ;---
    ; Sort the final output player spell names stringlist.
        StorageUtil.StringListSort(None, "HAC_Spells")
    
    ; insert at index 0 of stringlist the escape menu as way out
        StorageUtil.StringListInsert(None, "HAC_Spells", 0, "Back / Cancel / No Change")
    ;---
    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 CreateFormList()
    ;------------------------
        actor player = Game.GetPlayer()
    
        spell[] a = PO3_SKSEFunctions.GetAllSpells()                  ; Build a temp array with all spells in the game
        StorageUtil.FormListClear(None, "HAC_SpForms")                ; *** clear *** forms
        
        int i = a.Length                                              ; <array>.Length returns always a value greater or equal to ZERO
        WHILE (i)            ; (i) is equal to (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
                    StorageUtil.FormListAdd(None, "HAC_SpForms", akSpell as Form, False)        ; do not allow duplicates in list
                ENDIF
            ENDIF
        ENDWHILE
    EndFunction
    
    
    ;--------------------------
    Function Forms_To_Strings()
    ;--------------------------
        int m = StorageUtil.FormListCount(None, "HAC_SpForms")        ; get length of unsorted formlist
    ;;;    StorageUtil.FormListClear(None, "HAC_Spells")              ; *** clear *** spells  , removed this line, because of wrong function!
       StorageUtil.StringListClear(None, "HAC_Spells")                ; *** clear *** spells
    
    ; use the formlist of spells to build a new stringlist of spellnames
        int i = 0
        WHILE (i < m)
            spell akSpell = StorageUtil.GetFormValue(None, StorageUtil.FormListGet(None, "HAC_SpForms", i)) as Spell
            IF ( akSpell )
                Update_HAC_Spells(akSpell)
                i = i + 1
            ELSE
                StorageUtil.FormListRemoveAt(None, "HAC_SpForms", i)
                m = m - 1    
            ENDIF
        ENDWHILE
    EndFunction

     

     

  7. (1) Keep in mind: If I rewrite the papyrus code I thought something about it. My version do the same, but is faster code.

            If (akSpell) && akSpell.GetNthEffectMagicEffect(0).GetCastingType()     ; get the first MagicEffect of the spell

    your version

            If (akSpell) && akSpell.GetNthEffectMagicEffect(0).GetCastingType() > 0 ; get the first MagicEffect of the spell

    (2) about event handling, try this:

     

     

    ;Import PO3_Events_Alias
    ;Import PO3_SKSEFunctions
    
    ;Import StorageUtil
    
        ; StorageUtil name: "HAC_Spells"         ; list of strings
        ; StorageUtil name: "HAC_SpForms"        ; list of forms
    
    
    ; -- 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 mod was loaded by savegame
        RegisterForSingleUpdate(1.0)
    ENDEVENT
    
    EVENT OnUpdate()
        Debug.Notification("HAC: Starting Spell Maintenance")
        RefreshAllSpells()
        Debug.Notification("HAC: Spell Maintenance Completed")
    
        PO3_Events_Alias.RegisterForSpellLearned(self)               ; register for special event
    ENDEVENT
    
    
    EVENT OnSpellLearned(Spell akSpell)  ; event added by Papyrus Script Extender
    
        AddTo_HAC_Spells(akSpell)                                    ; add the spell to storageutil stringlist
        StorageUtil.StringListSort(None, "HAC_Spells")               ; Sort the final output spell names stringlist.
        Strings_To_Forms()                                           ; Clear storageutil list of spell forms and add new forms
    
    ENDEVENT

     


    (3) Its surely faster to store only one formlist like "HAC_SpForms" and create a temporary Stringlist like "HAC_Spells" on th fly, if its recomended by MCM menu. But without to see code dependencies its a hint only.

  8. IA710 wrote: "Any suggestions are welcome."

     

    Maybe you remember to an older MCM posting, I wrote similiar 'use functions to separate code and reduce redundancy'.

    I think the use of "HAC_PlSpellForms" does not make sense. Link to storageutil papyrus source: https://github.com/Vicyntae/SCLSE/blob/master/Data/Source/Scripts/StorageUtil.psc

     

    My optimizations as follow:

        ; PapyrusUtil name: "HAC_Spells"         ; list of strings
        ; PapyrusUtil name: "HAC_SpForms"        ; list of forms
    
    EVENT OnSpellLearned(Spell akSpell)  ; event added by Papyrus Script Extender
    ;===================
    ; add the spell to storageutil stringlist
        myF_AddTo_HAC_Spells(akSpell)
    
    ; Sort the final output spell names stringlist.
        StorageUtil.StringListSort(None, "HAC_Spells")
    
    ; Clear storageutil list of spell forms and add new forms
        myF_Strings_To_Forms()
    ENDEVENT
    
    
    ;-------------------------------------------
    FUNCTION myF_AddTo_HAC_Spells(Spell akSpell)  ; helper
    ;-------------------------------------------
        string s = akSpell.GetName()
    
    ; Make sure that the spell name string is mapped to the correct formid
        StorageUtil.SetFormValue(None, s, akSpell as Form)
    
    ; Remove index 0 of the final output stringlist
        StorageUtil.StringListShift(None, "HAC_Spells")
    
    ; Add the spell name to the final output stringlist with allowdupes FALSE
        StorageUtil.StringListAdd(None, "HAC_Spells", s, False)
    ENDFUNCTION
    
    
    ;--------------------------
    FUNCTION RefreshAllSpells()
    ;--------------------------
    ; Clear storageutil stringlist and add new spell strings
        myF_CreateSpellList()
    
    ; Sort the final output player spell names stringlist.
        StorageUtil.StringListSort(None, "HAC_Spells")
    
    ; Clear storageutil list of spell forms and add new forms
        myF_Strings_To_Forms()
    ENDFUNCTION
    
    
    ;--------------------------------------------
    FUNCTION myF_Update_HAC_Spells(Spell akSpell)  ; helper
    ;--------------------------------------------
        string s = akSpell.GetName()                                   ; get the name and use storageutil functions
        StorageUtil.SetFormValue(None, s, akSpell as Form)             ; convert formid value of the spell as string:formid map
        StorageUtil.StringListAdd(None, "HAC_Spells", s, False)        ; add spellname to stringlist
    ENDFUNCTION
    
    
    ;-----------------------------
    FUNCTION myF_CreateSpellList()  ; helper
    ;-----------------------------
        actor player = Game.GetPlayer()
        StorageUtil.StringlistClear(None, "HAC_Spells")                ; *** clear *** strings
    
    ; Build an array with all spells in the game
        spell[] a = PO3_SKSEFunctions.GetAllSpells()
    
        int i = a.Length        ; a = allSpellsInTheGame
        WHILE (i)
            i = i - 1
            spell akSpell = a[i]
    
            IF (akSpell) && akSpell.GetNthEffectMagicEffect(0).GetCastingType()  ; get the first MagicEffect of the spell
                ; 1 = Fire and Forget
                ; 2 = Concentration
    
                ; check every playable spell from master list to see if the player has it
                IF player.HasSpell(akSpell)
                    myF_Update_HAC_Spells(akSpell)
                ENDIF
    ;;;        ELSE
                ; 0 = constant effect, we are not interested in this spell
            ENDIF
        ENDWHILE
    ENDFUNCTION
    
    
    ;------------------------------
    FUNCTION myF_Strings_To_Forms()  ; helper
    ;------------------------------
        int m = StorageUtil.StringListCount(none, "HAC_Spells")
        StorageUtil.FormListClear(None, "HAC_SpForms")                ; *** clear *** forms
    
    ; using the sorted string list to build the final output player spell formlist
        int i = 0
        WHILE (i < m)
            form fm = StorageUtil.GetFormValue(none, StorageUtil.StringListGet(None, "HAC_Spells", i) )
            IF ( fm )
                StorageUtil.FormListAdd(None, "HAC_SpForms", fm)
            ENDIF
            i = i + 1
        ENDWHILE
    
    ; insert at index 0 of sorted stringlist an escape menu as backdoor
        StorageUtil.StringListInsert(None, "HAC_Spells", 0, "Back / Cancel / No Change")
    ENDFUNCTION

  9. Hmm.. to get them work you have to change the script from extending objectReference into actor (I edited my previous posting)!

    But that is out of "good coding practice" for the player or any other unique vanilla actor.

    https://www.creationkit.com/index.php?title=OnObjectEquipped_-_Actor

    So we have to change the whole script as follow.

    https://www.creationkit.com/index.php?title=OnEquipped_-_ObjectReference

     

    colo_FluidAnimationScript v2

     

    Scriptname colo_FluidAnimationScript extends ObjectReference
    ; https://forums.nexusmods.com/index.php?/topic/10877768-help-with-scripting/
    ; version 2
    
      Idle PROPERTY IdleStop_Loose auto    ; added 2022-01-31, missing in older post
    
    
    ; -- EVENTs -- 2 + "Waiting" + "WearItem"
    
    EVENT OnContainerChanged(ObjectReference akNewContainer, ObjectReference akOldContainer)
        IF (akNewContainer == Game.GetPlayer() as ObjectReference)
            gotoState("Waiting")        ; ### STATE ###  player got the item, observe equip now
        ELSE
            UnRegisterForUpdate()  ; just in case
            gotoState("")               ; ### STATE ###
        ENDIF
    ENDEVENT
    
    
    EVENT OnUpdate()
        Game.GetPlayer().PlayIdle(IdleStop_Loose)         ; after 20 sec the item is wearing, play vanilla stop animation
        Utility.Wait(0.1)
    ENDEVENT
    
    
    ;===================================
    State Waiting
    ;============
        EVENT OnEquipped(Actor akActor)
            gotoState("WearItem")    ; ### STATE ###
            myF_Action(1)
            RegisterForSingleUpdate(20.0)                 ; wait for 20 sec
        ENDEVENT
    ;=======
    endState
    
    
    ;===================================
    State WearItem
    ;=============
        EVENT OnUnEquipped(Actor akActor)
            UnRegisterForUpdate()
            gotoState("Waiting")    ; ### STATE ###
            myF_Action(0)
        ENDEVENT
    ;=======
    endState
    
    
    ; -- FUNCTION --
    
    ;-------------------------
    FUNCTION myF_Action(Int i)
    ;-------------------------
        IF ( i )    ; i == 1
            Debug.SendAnimationEvent(Game.GetPlayer(), "EFF")        ; if item equipped play special animation
        ELSE        ; i == 0
            Debug.SendAnimationEvent(Game.GetPlayer(), "EFFstop")    ; if item will be un-equipped play special stop animation
        ENDIF
    ENDFUNCTION

     

     

  10. (1) you wrote: "I'm VERY NEW to scripting."

    Well.. a reason, but no thank you or any other good sign of you. I am really disappointed, in older days your postings were called "script kiddies"!

    original scriptnames

       DF_Script_Trophy_Base.psc
       DF_Script_Trophy_Misc.psc
       DF_Script_Trophy_Activator.psc

    my scriptnames

      wilw_Trophy_Base.psc
      wilw_Trophy_Misc.psc
      wilw_Trophy_Activator.psc
    (oRef as wilw_Trophy_Activator).myF_Init(i)        ; <-- By changing script name adjust here !!!

    your scriptnames

      cg_Trophy_Base.psc
      cg_Trophy_Misc.psc
      cg_Trophy_Activator.psc
    (oRef as cg_Trophy_Activator).myF_Init(i)        ; <-- By changing script name adjust here !!!

    (2) you wrote: "how do i make a spoiler tag"

    Did you have a brain? If yes, use an internet web searcher or the forum search function (right on top).

     

     

    [ spoiler ]

    hidden posting, to make it work remove the spaces within the brackets

    [ /spoiler ]

     

     

  11. DF_Script_Trophy_Activator

     

    Scriptname DF_Script_Trophy_Activator extends ObjectReference
    {This script deletes the trophy being activated and sets its global back to 0.}
    
    
    ;Original script created by Darkfox127
    ;Any potential problems which may arise from this script being changed from the original are the sole responsibility of the mod author making the changes.
    
    
    GlobalVariable Property Global_Trophy_Placed_01 Auto
    {The trophy global for the trophy base to know if a trophy is already in place.}
    GlobalVariable Property Global_Trophy_Placed_02 Auto
    {The trophy global for the trophy base to know if a trophy is already in place.}
    
    Int Property XPos_TrophyMarker_01 Auto
    {The X axis position of the X-Marker used for the first trophy base.}
    Int Property XPos_TrophyMarker_02 Auto
    {The X axis position of the X-Marker used for the second trophy base.}
    
    LeveledItem Property Trophy_Craft_Items Auto
    {A leveled list containing all of the items used for creating the trophy.}
    
    ObjectReference Property PlayerREF Auto
    {The default reference for the game to point toward the player.}
    
    String Property ReturnMSG Auto
    {The notification to show when the craft items have been returned to the player.}
    
    
    
    Event OnActivate(ObjectReference akActionRef)
    
        If (Self.GetPositionX() == XPos_TrophyMarker_01)
            Global_Trophy_Placed_01.SetValue(0) ;Set the trophy global to 0 to allow the base to know a new trophy can be placed
        ElseIf (Self.GetPositionX() == XPos_TrophyMarker_02)
            Global_Trophy_Placed_02.SetValue(0) ;Set the trophy global to 0 to allow the base to know a new trophy can be placed
        EndIf
    
        PlayerREF.AddItem(Trophy_Craft_Items, 1, True) ;Silently give the player all of the items back which were used in this trophys creation
        debug.notification(ReturnMSG) ;Show a notification to inform the player their items have been returned to them
        Self.delete() ;The trophy now deletes itself and is no longer visible on the trophy base, allowing for a new trophy to be put down
        
    EndEvent

     

    wilw_Trophy_Activator

     

    Scriptname wilw_Trophy_Activator extends ObjectReference
    {v1.0 ReD 2022-01-21}  ; This script deletes the trophy being activated and sets its global back to 0.
    
    ; https://www.nexusmods.com/skyrim/mods/79519?tab=files
    ; "Ressource Pack" made by Darkfox127
    
    ; Original created by Darkfox127
    ; Any potential problems which may arise from this script being changed from the
    ; original are the sole responsibility of the mod author making the changes.
    ;-------------------------------------------------------------------------------
    
      LeveledItem PROPERTY Trophy_Craft_Items auto                        ; A leveled list containing all of the items used for creating the trophy.
      String PROPERTY ReturnMSG = "You got all trophy items back!" auto   ; {The notification to show when the craft items have been returned to the player.}
    
    ; -----------------------------------------------------
     ;;Int Property XPos_TrophyMarker_01 Auto                      ; {The X axis position of the X-Marker used for the first trophy base.}
     ;;Int Property XPos_TrophyMarker_02 Auto                      ; {The X axis position of the X-Marker used for the second trophy base.}
    
    ; v1
     ;;GlobalVariable PROPERTY Global_Trophy_Placed_01 Auto        ; {The trophy global for the trophy base to know if a trophy is already in place.}
     ;;GlobalVariable PROPERTY Global_Trophy_Placed_02 Auto        ; {The trophy global for the trophy base to know if a trophy is already in place.}
     ;; ..
     ;;GlobalVariable PROPERTY Global_Trophy_Placed_21 Auto        ; {The trophy global for the trophy base to know if a trophy is already in place.}
    
    ; v2
      GlobalVariable[] PROPERTY Array_Global_Trophy_Placed auto        ; array of Globals pre-placed by CreationKit
      ; Array_Global_Trophy_Placed[0] = Global_Trophy_Placed_01
      ; ..
      ; Array_Global_Trophy_Placed[20] = Global_Trophy_Placed_21
    
    ; next properties will be set during runtime
      GlobalVariable PROPERTY Global_Trophy_Placed auto Hidden    ; {The trophy global for the trophy base to know if a trophy is already in place.}
    
    
    ; -- EVENTs -- (3 of 3)
    
    EVENT OnInit()
    ;;    Debug.Trace(" OnInit() - has been reached.. " +self)        ; for debugging only
    ENDEVENT
    
    
    EVENT OnActivate(ObjectReference akActionRef)
    IF (akActionRef == Game.GetPlayer() as ObjectReference)
    ELSE
        RETURN    ; - STOP -    not player activated!
    ENDIF
    ;---------------------
        self.Disable(TRUE)                    ; fade it out
    
    ; silently give the player all of the items back, which were used in this creation of trophy.
        akActionRef.AddItem(Trophy_Craft_Items as Form, 1, TRUE)    ; all goods/materials back to player
    
    ; set the trophy global to 0 to allow the base to know a new trophy can be placed
        Global_Trophy_Placed.SetValue(0.0)
    
        Debug.Notification(ReturnMSG)        ; inform the player, that trophy material items have been returned to the inventory
    
    ; The trophy now deletes itself and is no longer visible on the trophy base, allowing for a new trophy to be put down
        self.Delete()
    ENDEVENT
    
    
    ; -- FUNCTION --
    
    ;-----------------------
    FUNCTION myF_Init(Int i)  ; v2, external called by "wilw_Trophy_Misc.psc"
    ;-----------------------
        IF (i >= 0) && (i < Array_Global_Trophy_Placed.Length)
            Global_Trophy_Placed = Array_Global_Trophy_Placed[i]
            Global_Trophy_Placed.SetValue(1.0)
        ELSE
            Debug.Trace(" myF_Init() - Error: array index is out of bound! " +i)
        ENDIF
    
    ; now destroy the array of globalVars, no longer needed!
        GlobalVariable[] b
        Array_Global_Trophy_Placed = b
    ENDFUNCTION
    
    
    ;;-----------------------
    ;FUNCTION myF_Init(Int i)  ; v1, external called by "wilw_Trophy_Misc.psc"
    ;;-----------------------
    ;    IF     (i == 1)
    ;        Global_Trophy_Placed = Global_Trophy_Placed_01
    ;    ELSEIF (i == 2)
    ;        Global_Trophy_Placed = Global_Trophy_Placed_02
    ;
    ;    ..
    ;
    ;    ELSEIF (i == 21)
    ;        Global_Trophy_Placed = Global_Trophy_Placed_21
    ;    ENDIF
    ;
    ;    Global_Trophy_Placed.SetValue(1.0)
    ;ENDFUNCTION

     

     

     

    Advantage you do not have to deal with a lot of "X axis position of the X-Marker".

  12. Sometimes its a good idea to use spoiler tags and its always fine to post a link where to find the original scripts. Keep it in mind!

     

    mod: "Ressource Pack" made by Darkfox127 https://www.nexusmods.com/skyrim/mods/79519?tab=files
    video: "Creation Kit (Custom Trophy Stands)", How to create multiple trophies? https://www.youtube.com/watch?v=-4igxAUcJ3A

     

    The trophy creation is using next three scripts.

      1 - DF_Script_Trophy_Base.psc
      2 - DF_Script_Trophy_Misc.psc
      3 - DF_Script_Trophy_Activator.psc

    At first do not use the scriptnames (from above) provided by ressource pack. Make it unique enough like next:

      wilw_Trophy_Base.psc
      wilw_Trophy_Misc.psc
      wilw_Trophy_Activator.psc

    Now I changed the original scripts in some code parts to make it easier to handle. You have to use an array and also to create a formlist.

     

    DF_Script_Trophy_Base

     

    Scriptname DF_Script_Trophy_Base extends ObjectReference
    {Checks if a trophy is in place. If there is no trophy in place, this allows the player to add one.}
    
    
    ;Original script created by Darkfox127
    ;Any potential problems which may arise from this script being changed from the original are the sole responsibility of the mod author making the changes.
    
    
    GlobalVariable Property Global_Trophy_Placed Auto
    {DEFAULT: 0 This will be set to 1 once a trophy is placed to allow this base to know if a trophy is in place.}
    
    GlobalVariable Property Global_Trophy_Active Auto
    {This will allow the crafting menu to know which base to place the trophy on.}
    
    Int Property Int_Trophy_Tag Auto
    {Which trophy stand you want this trophy base to control.}
    
    String Property FailMSG Auto
    {The message you want to display if the player activates this base and a trophy is already in place.}
    
    
    
    Event OnActivate(ObjectReference akActionRef)
    
            If (Global_Trophy_Placed.GetValue() == 1) ;Is a trophy already in place?
                debug.notification(FailMSG) ;Inform the player they cannot add a trophy is one is already in place
            Else
                Global_Trophy_Active.SetValue(Int_Trophy_Tag) ;The trophy base you are going to be adding a trophy to
                Utility.Wait(0.1) ;Short delay to allow global changes to take effect
                Self.GetLinkedRef().Activate(Game.GetPlayer()) ;Activates the craft system that your linked ref should point to
            EndIf
    
    EndEvent

     

    wilw_Trophy_Base

     

    Scriptname wilw_Trophy_Base extends ObjectReference
    {v1.0 ReD 2022-01-21}  ; check if a trophy is in place. If there is no trophy in place, this allows the player to add one.
    
    ; https://www.nexusmods.com/skyrim/mods/79519?tab=files
    ; "Ressource Pack" made by Darkfox127
    
    ; https://www.youtube.com/watch?v=-4igxAUcJ3A
    ; "Creation Kit (Custom Trophy Stands)", How to create multiple trophies?
    
    ; 1 "DF_Script_Trophy_Base.psc"
    ; 2 "DF_Script_Trophy_Misc.psc"
    ; 3 "DF_Script_Trophy_Activator.psc"
    
    ; Original created by Darkfox127
    ; Any potential problems which may arise from this script being changed from the
    ; original are the sole responsibility of the mod author making the changes.
    ;-------------------------------------------------------------------------------
    
      GlobalVariable PROPERTY Global_Trophy_Active auto          ; global.GetValue() is 0.0 by default
      ; This will be set to a value of Trophy_Tag, which allows the "crafting menu" to know the base to place the trophy on.
    
      Int PROPERTY Int_Trophy_Tag auto                           ; [default=0], {Which trophy stand you want this trophy base to control.}
      ; 1
      ; 2
      ; ..
      ; 21
    
      GlobalVariable PROPERTY Global_Trophy_Placed auto          ; This globalVar will be set by "DF_Script_Trophy_Activator.psc" to a value of 1.0
      ; Global_Trophy_Placed_01                                  ; once a trophy is placed to allow this base to know if the trophy is in place.
      ; Global_Trophy_Placed_02
      ; ..
      ; Global_Trophy_Placed_21
    
      String PROPERTY FailMSG = "Trophy already placed!" auto    ; {The message you want to display if the player activates this base and a trophy is already in place.}
    
    
    ; -- EVENTs -- (1 of 3)
    
    EVENT OnActivate(ObjectReference akActionRef)
        myF_Action(akActionRef)
    ENDEVENT
    
    
    ; -- FUNCTION --
    
    ;-----------------------------------------------
    FUNCTION myF_Action(ObjectReference akActionRef)
    ;-----------------------------------------------
    IF (akActionRef == Game.GetPlayer() as ObjectReference)
    ELSE
        RETURN    ; - STOP - /1    not player activated!
    ENDIF
    ;---------------------
    IF (Global_Trophy_Placed.GetValueInt() == 1)
        Debug.Notification(FailMSG)
        RETURN    ; - STOP - /2    you cannot add another trophy here, its already in place.
    ENDIF
    ;---------------------
    IF (Int_Trophy_Tag <= 0)
        Debug.Notification("Illegal trophy tag.. " +Int_Trophy_Tag)
        RETURN    ; - STOP - /3
    ENDIF
    ;---------------------
        Global_Trophy_Active.SetValueInt( Int_Trophy_Tag )        ; The trophy base you are going to be adding a trophy to
        Utility.Wait(0.1)                                         ; Short delay to allow global changes to take effect
    
    ; at least we have to activate the craft system that your linkedRef should point to
        self.GetLinkedRef().Activate(akActionRef)
    ENDFUNCTION

     

     

     

    DF_Script_Trophy_Misc

     

    Scriptname DF_Script_Trophy_Misc extends ObjectReference
    {This script creates a trophy and places it on the trophy base currently being activated.}
    
    
    ;Original script created by Darkfox127
    ;Any potential problems which may arise from this script being changed from the original are the sole responsibility of the mod author making the changes.
    
    
    Activator Property Trophy_To_Place Auto
    {The trophy to create and place on the base.}
    
    GlobalVariable Property Global_Trophy_Active Auto
    {This will check to see which trophy base is currently set to active so that this craft menu knows where to place the trophy.}
    GlobalVariable Property Global_Trophy_InUse Auto
    {A global which is set after crafting an item to prevent the player from creating more than one trophy on the same stand.}
    GlobalVariable Property Global_Trophy_Placed_01 Auto
    {The trophy global for the trophy base to know if a trophy is already in place.}
    GlobalVariable Property Global_Trophy_Placed_02 Auto
    {The trophy global for the trophy base to know if a trophy is already in place.}
    
    MiscObject Property Trophy_Item Auto
    {This is the item created by the crafting menu which triggers these events. This is the item this script is attached to.}
    
    ObjectReference Property TrophyMarker_01 Auto
    {This will be one of the X-Marker Headings that your trophy is placed on.}
    ObjectReference Property TrophyMarker_02 Auto
    {This will be one of the X-Marker Headings that your trophy is placed on.}
    ObjectReference Property PlayerREF Auto
    {The default reference for the game to point toward the player.}
    
    
    
    Event OnContainerChanged(ObjectReference akNewContainer, ObjectReference akOldContainer)
    
        If (Global_Trophy_Active.GetValue() == 0) ;Is the current base tagged as 0?
    
            debug.notification("Global Set Wrong")
    
        ElseIf (Global_Trophy_Active.GetValue() == 1) ;Is the current base tagged as 1?
    
            TrophyMarker_01.PlaceAtMe(Trophy_To_Place) ;Place the trophy at the relevant marker
            Global_Trophy_Placed_01.Setvalue(1) ;Set base one as trophy placed
            PlayerREF.RemoveItem(Trophy_Item,1,True) ; Removes the trophy misc item from the players inventory silently
    
        ElseIf (Global_Trophy_Active.GetValue() == 2) ;Is the current base tagged as 1?
    
            TrophyMarker_02.PlaceAtMe(Trophy_To_Place) ;Place the trophy at the relevant marker
            Global_Trophy_Placed_02.Setvalue(1) ;Set base two as trophy placed
            PlayerREF.RemoveItem(Trophy_Item,1,True) ; Removes the trophy misc item from the players inventory silently
    
        EndIf
    
        Global_Trophy_InUse.SetValue(1)
        Utility.Wait(0.1)
        Global_Trophy_InUse.SetValue(0)
    
    EndEvent

     

    wilw_Trophy_Misc

     

    Scriptname wilw_Trophy_Misc extends ObjectReference
    {v1.0 ReD 2022-01-21}  ; This script creates a trophy and places it on the trophy base currently being activated.
    
    ; https://www.nexusmods.com/skyrim/mods/79519?tab=files
    ; "Ressource Pack" made by Darkfox127
    
    ; Original created by Darkfox127
    ; Any potential problems which may arise from this script being changed from the
    ; original are the sole responsibility of the mod author making the changes.
    ;-------------------------------------------------------------------------------
    
      GlobalVariable PROPERTY Global_Trophy_InUse  auto        ; global.GetValue() is 0.0 by default
      ; after crafting an item, prevent the player from creating more than one trophy on the same stand.
    
      GlobalVariable PROPERTY Global_Trophy_Active auto        ; value was already set by "DF_Script_Trophy_Base.psc"
      ; This will check to see which trophy base is currently set to active so that this craft menu knows where to place the trophy.
     
      Activator PROPERTY Trophy_To_Place auto                  ; to create a new object which has attached "DF_Script_Trophy_Activator.psc"
      ; The trophy to create and place at right position.
    
      MiscObject PROPERTY Trophy_Item auto                     ; I am the item created by the crafting menu which triggers these events.
      {This is the baseObject this script is attached to.}
    
    ; -----------------------------------------------------
     ;;GlobalVariable PROPERTY Global_Trophy_Placed_01 auto    ; {The trophy global for the trophy base to know if a trophy is already in place.}
     ;;GlobalVariable PROPERTY Global_Trophy_Placed_02 auto    ; {The trophy global for the trophy base to know if a trophy is already in place.}
    
    ; v1
     ;;ObjectReference PROPERTY TrophyMarker_01 auto        ; {This will be one of the X-Marker Headings that your trophy is placed on.}
     ;;ObjectReference PROPERTY TrophyMarker_02 auto        ; {This will be one of the X-Marker Headings that your trophy is placed on.}
     ;; ..
     ;;ObjectReference PROPERTY TrophyMarker_21 auto        ; {This will be one of the X-Marker Headings that your trophy is placed on.}
    
    ; v2a
     ;;ObjectReference[] PROPERTY Array_TrophyMarkers auto       ; array of TrophyMarkers pre-placed by CreationKit
      ; Array_TrophyMarkers[0] = TrophyMarker_01
      ; ..
      ; Array_TrophyMarkers[20] = TrophyMarker_21
    
    ; v2b
      FormList PROPERTY TrophyMarkerList auto                    ; you have to create the formlist by CreationKit and assign to this property as well
      ; TrophyMarkerList.GetAt(0) = TrophyMarker_01
      ; ..
      ; TrophyMarkerList.GetAt(20) = TrophyMarker_21
    
    
    ; -- EVENTs -- (2 of 3)
    
    EVENT OnInit()
    ;;    Debug.Trace(" OnInit() - has been reached.. " +self)        ; for debugging only
    ENDEVENT
    
    
    EVENT OnContainerChanged(ObjectReference akNewContainer, ObjectReference akOldContainer)
        IF myF_IsOK(akNewContainer)
            Global_Trophy_InUse.SetValue(1.0)
            Utility.Wait(0.1)
            Global_Trophy_InUse.SetValue(0.0)
        ENDIF
    ENDEVENT
    
    
    ; -- FUNCTIONs -- 2
    
    ;-----------------------------------------------------
    Bool FUNCTION myF_IsOK(ObjectReference akNewContainer)
    ;-----------------------------------------------------
    IF (akNewContainer == Game.GetPlayer() as ObjectReference)
    ELSE
        Return False    ; /1    its not the players inventory
    ENDIF
    ;---------
        int i = Global_Trophy_Active.GetValueInt()
    
    IF (i == 0)
        Debug.Notification("Trophy is not active.. GlobalVar setting is wrong!")
        Return False    ; /2
    ENDIF
    ;---------
    
    ; v1
    ;    IF     (i == 1)
    ;        myF_Place(TrophyMarker_01, i)
    ;    ELSEIF (i == 2)
    ;        myF_Place(TrophyMarker_02, i)
    ;    ENDIF
    
    ; v2a, v2b
        myF_Place(akNewContainer, i)
    
        Return TRUE        ; /3    trophy successful placed
    ENDFUNCTION
    
    
    ;--------------------------------------------------------
    FUNCTION myF_Place(ObjectReference akNewContainer, Int i)  ; v2
    ;--------------------------------------------------------
    ; silently remove the trophy misc item from players inventory
        akNewContainer.RemoveItem(Trophy_Item as Form, 1, TRUE)
    
    ; place a trophy at the relevant marker
        i = i - 1                                                ; adjust start position
    
    ;;    objectReference oRef = Array_TrophyMarkers[i]                            ; get the marker from array
        objectReference oRef = TrophyMarkerList.GetAt(i) as ObjectReference        ; get the marker by formlist
    
        IF ( oRef )
            oRef = oRef.PlaceAtMe(Trophy_To_Place as Form, 1)    ; place a new object "the trophy" depends on this misc item
            IF ( oRef )
                (oRef as wilw_Trophy_Activator).myF_Init(i)        ; <-- By changing script name adjust here !!!
            ELSE
                Debug.Notification("Failed to place the trophy!")
            ENDIF
        ELSE
            Debug.Notification("Failed to get a valid trophy marker!")
        ENDIF
    ENDFUNCTION
    
    
    ;;----------------------------------------------
    ;FUNCTION myF_Place(ObjectReference oRef, Int i)  ; v1
    ;;----------------------------------------------
    ;; silently remove the trophy misc item from players inventory
    ;    Game.GetPlayer().RemoveItem(Trophy_Item as Form, 1, TRUE)
    ;
    ;; place a trophy at the relevant marker
    ;    oRef = oRef.PlaceAtMe(Trophy_To_Place as Form, 1)
    ;    IF ( oRef )
    ;        (oRef as wilw_Trophy_Activator).myF_Init(i)            ; <-- By changing script name adjust here !!!
    ;    ELSE
    ;        Debug.Notification("Failed to place the trophy!")
    ;    ENDIF
    ;ENDFUNCTION

     

     

  13. As Sphered already mentioned: What ever you do, do never use x, y or z as variable or property names within scripts of type objectreference!

     

    Its caused by vanilla script "ObjectReference.psc", so the papyrus compiler handles x as self.X etc.

     

     

    Scriptname ObjectReference extends Form Hidden
    
    ; Property to obtain the current X position of the object
    float Property X
      float Function get()
        return GetPositionX()
      EndFunction
    EndProperty
    
    ; Property to obtain the current Y position of the object
    float Property Y
      float Function get()
        return GetPositionY()
      EndFunction
    EndProperty
    
    ; Property to obtain the current Z position of the object
    float Property Z
      float Function get()
        return GetPositionZ()
      EndFunction
    EndProperty

     

     

  14. while (i >= 0)
    ;;; Schedule = BB_ActiveSchedules.GetAt(0) As Quest           ; you code line
        quest q = BB_ActiveSchedules.GetAt(i) As Quest
        referenceAlias RA = q.GetAlias(0) As ReferenceAlias
        IF ( RA )
           RA.Clear()
        ENDIF
        i = i - 1
    endwhile

    just another approach by using array

     

    https://forums.nexusmods.com/index.php?/topic/10976043-defined-property-returning-as-none/
    
      GlobalVariable  PROPERTY AtLocation auto
      ObjectReference PROPERTY MapMarker  auto
    
      FormList PROPERTY ScheduleQuests auto
    
      Quest[] BB_ActiveSchedules
    
      Bool bBusy                                ; [default=False]
    
    
    FUNCTION InitArray()
    ;-------------------
        BB_ActiveSchedules = new Quest[100]
    ENDFUNCTION
    
    
    FUNCTION DestroyArray()
    ;----------------------
        quest[] b
        BB_ActiveSchedules = b
    ENDFUNCTION
    
    
    ;------------------------
    FUNCTION BB_SetSchedule()
    ;------------------------
        WHILE (bBusy)
            Utility.Wait(0.1)
        ENDWHILE
        bBusy = TRUE
    ;    -------
        quest q = ScheduleQuests.GetAt( AtLocation.GetValueInt() ) as Quest         ; get the schedule quest
        referenceAlias RA = q.GetAlias(0) as ReferenceAlias                         ; get related alias
        IF ( RA )
            RA.ForceRefTo(MapMarker)                                                ; refer to marker
        ENDIF
    
        IF (BB_ActiveSchedules.Length == 0)
            InitArray()
        ENDIF
    
        int i = BB_ActiveSchedules.Find(None)
        IF (i >= 0)
            BB_ActiveSchedules[i] = q                                               ; store to array as active quest
        ENDIF
    ;    -------
        bBusy = False
    ENDFUNCTION
    
    
    ;--------------------------
    FUNCTION BB_ClearSchedule()
    ;--------------------------
    IF ( bBusy )
        RETURN    ; - STOP -    Do not clear more than once!
    ENDIF
    ;---------------------
        bBusy = TRUE
    ;    -------
        quest q
    
        int i = 0
        WHILE (i < BB_ActiveSchedules.Length)
            q = BB_ActiveSchedules[i]
            IF ( q )
                referenceAlias RA = q.GetAlias(0) as ReferenceAlias
                IF ( RA )
                    RA.Clear()
                ENDIF
        
                q = None
                BB_ActiveSchedules[i] = q
        
                i = i + 1
            ELSE
                i = BB_ActiveSchedules.Length
            ENDIF
        ENDWHILE
    
        i = BB_ActiveSchedules.Find(None)
        IF (i == -1)
            RETURN    ; - STOP -    Array is full!
        ENDIF
    ;    ----------------------
        int n = i                                ; position of first <None> entry
        i = i + 1                                ; get next array position (init)
        WHILE (i < BB_ActiveSchedules.Length)
            q = BB_ActiveSchedules[i]
            IF ( q )
                BB_ActiveSchedules[n] = q        ; compact array by moving entry down
                n = n + 1                        ; increase compact counter
            ENDIF
            i = i + 1                            ; get next array position (update)
        ENDWHILE
    ;    -------
        bBusy = False
    ENDFUNCTION

     

     

  15. The question is

    a) one baseobject "Trophy_Item" or

    b) 21 different baseobjects

    with 21 markers to place a single trophy?

     

    MiscObject Property Trophy_Item Auto
    {This is the item created by the crafting menu which triggers these events. This is the item this script is attached to.}
    And are the GlobalVariables used within these two scripts only?
    GlobalVariable Property Global_Trophy_Placed_0x Auto
    {The trophy global for the trophy base to know if a trophy is already in place.}
  16. I would suggest next code for this script. Keep in mind the script "DLC2ReferenceAliasArrayScript" has the same bug!

     

    DLC1ReferenceAliasArrayScript

     

    Scriptname DLC1ReferenceAliasArrayScript extends Quest  
    {v1.5 ReDragon 2021}    ; *** jduvall -- please talk to me before changing this script
    
    ; IMPORTANT! This needs to be an array of empty values with the same number of values as aliases in AliasArray.
    
      ReferenceAlias[]  PROPERTY AliasArray auto    ; All the aliases that make up the "array"
      ObjectReference[] PROPERTY RefArray   auto
    
    ; "We are using a seperate array to hold references and shuffling refs around in that because it's much less
    ;  overhead than shuffling refs into and out of aliases - requested by mlipari and jlundin" (Bethesda)
    
      Bool threadLock
    
    
    ; -- FUNCTIONs -- 3 + 1 + 2
    
      Bool PROPERTY TraceAliasRefs auto                ; [Default=False] for debugging
        ; if TRUE, will debug.trace() all the aliases in the array after 'ForceRefInto()' or 'ClearRefFrom()'
    
    ;------------------------
    FUNCTION TraceArrayRefs()    ; for debugging only
    ;------------------------
    ;int i = 0
    ;    WHILE (i < AliasArray.Length)
    ;;;        Debug.Trace("Alias: "+AliasArray[i]+" has "+AliasArray[i].GetReference())
    ;        i = i + 1
    ;    ENDWHILE
    ENDFUNCTION
    
    
    ;FUNCTION LockThread()  ; UnUSED by now
    ;ENDFUNCTION
    
    ;----------------------
    FUNCTION UnlockThread()
    ;----------------------
        threadLock = False
    ENDFUNCTION
    
    
    ;----------------------------------------------------
    ReferenceAlias FUNCTION FindRef(ObjectReference oRef)  ; oRef = RefToFind
    ;----------------------------------------------------
    int i = 0                                            ; i = index, search now from bottom to top
        WHILE (i < AliasArray.Length)
            referenceAlias RA = AliasArray[i]            ; RA = AliasToReturn
            IF ( RA )
                IF (oRef == RA.GetReference())
                    RETURN RA                            ; objRef was found (could be an unassigned alias, if oRef == None)
                ENDIF
            ENDIF
            i = i + 1
        ENDWHILE
    
    ;;    Debug.Trace(self+" FindRef() - Could not found " +oRef+ "..  RETURN <None>")
           RETURN None
    ENDFUNCTION
    
    
    ;################
    ;### ClearRef ########################################################################################
    ;################
    
    ;-----------------------------------------------
    Bool FUNCTION ClearRefFrom(ObjectReference oRef)  ; oRef = RefToClear
    ;-----------------------------------------------
    WHILE (threadLock)
        Utility.Wait(1.0)
    ENDWHILE
        threadLock = TRUE            ; *T*
    ;=======================
        referenceAlias RA = FindRef(oRef)                ; RA = FoundAlias
    
    IF ( RA )
        RA.Clear()                                       ; unassigned objRef from Alias
    ELSE
    ;;    Debug.Trace(self+" ClearRefFrom() - Did not find " +oRef+ " in any aliases.. RETURN False.")
        threadLock = False            ; ***    
        Return False
    ENDIF
    ;---------
        int i = RefArray.Find(oRef)                      ; find position
        RefArray[i] = None                               ; and clear the entry right now
    
    ;;;    RefArray[RefArray.Find(RefToClear)] = None       ; Buggy! (ReDragon)
    ;;==================================================
    ; http://www.creationkit.com/Arrays_%28Papyrus%29
    ; http://www.creationkit.com/index.php?title=Arrays_(Papyrus)
    ; The array Find() function cannot be used within an arrays element index brackets !!!
    ;
    ;    myArray[myArray.Find(none)] = newValue             ; no error on compiling, but ingame it will not work correctly and the value is not filled as expected.
    ;
    ;    int i = myArray.Find(None)                         ; find the first blank element
    ;    myArray[i] = newValue                              ; and fill it with our newValue
    ;;==================================================
    
        threadLock = False            ; ***
        Return TRUE
    ENDFUNCTION
    
    
    ;################
    ;### ForceRef ########################################################################################
    ;################ 2
    
    ;-----------------------------------------------
    Bool FUNCTION ForceRefInto(ObjectReference oRef)  ; oRef = RefToAdd
    ;-----------------------------------------------
    ; called by "DLC1VampireMesmerizeScript", "DLC1VampireTurnScript"
    
    WHILE (threadLock)
        Utility.Wait(1.0)
    ENDWHILE
        threadLock = TRUE            ; *T*
    ;=======================
        int i = RefArray.Find(oRef)                      ; i = foundRefIndex
    
    IF (i >= 0)
    ;;    Debug.Trace(self+" ForceRefInto("+i+") - " +oRef+ " was already found in RefArray..  RETURN False.")    
        threadLock = False            ; ***
        Return False
    ENDIF
    ;---------
        objectReference lastRef = PromoteRefs()          ; lastRef = RefToReplace
        referenceAlias  RA      = FindRef(lastRef)       ; RA = AliasToReplace
        lastRef = None
    
        IF ( RA )
    ;;        Debug.Trace(self + "ForceRefInto([" +oRef+ "]) will force reference into " +RA+ " which is also returned")
            RefArray[0] = oRef
            RA.ForceRefTo(oRef)
        ENDIF
    
        threadLock = False            ; ***
        RETURN (RA as Bool)                    ; returns TRUE or False, depends on valid RefAlias
    ENDFUNCTION
    
    
    ;-------------------------------------
    ObjectReference FUNCTION PromoteRefs()  ; internal only
    ;-------------------------------------
    objectReference lastRef                              ; lastRef = RefToReturn
    objectReference oRef                                 ; oRef = currentRef
    
        int i = RefArray.Length    - 1                   ; i = index
        IF (i >= 0)
            lastRef = RefArray[i]
        ENDIF
    
        WHILE (i > 0)                                    ; Do not allow a negative value here !!!
            oRef = RefArray[i - 1]
            IF ( oRef )
                RefArray[i] = oRef
            ENDIF
            i = i - 1
        ENDWHILE
    
        RETURN lastRef
    ENDFUNCTION

     

     

  17. Unfortuantely papyrus script language is not always very easy to understand. Very often is an 'improvement' of vanilla scripts, a step forward to get runtime errors with improved version.

    But here we have a papyrus compiler bug. https://web.archive.org/web/20200216210550/http://forums.bethsoft.com/topic/1610844-ckse-persisting-script-engine-bugs/

    THHIS BUG WAS NEVER SOLVED by any release of UNOFFICIAL PATCHES!

     

    Inci wrote (2016-Nov-01):
    "The compiler for SSE is still unable to properly manage temporary variables, when it compiles a nested function call inside an array index evaluation. Such a statement is compiled in a way, which causes temporary variable corruption during execution. This is the issue of 'myArray[myArray.Find(none)] = newValue' not working properly (although the same issue would actually affect a larger range of possible cases). Unfortunately, as the forum search functionality is still unavailable, I am unable to find and link to the thread, where this was originally discussed."

     

    DLC1ReferenceAliasArrayScript (vanilla code)

     

    Scriptname DLC1ReferenceAliasArrayScript extends Quest  
    
    ;jduvall -- please talk to me before changing this script
    
    ReferenceAlias[] Property AliasArray auto
    {All the arrays that make up the "array"}
    
    objectReference[] Property RefArray auto ;we are using a seperate array to hold references and shuffling refs around in that because its much less overhead than shuffling refs into and out of aliases - requested by mlipari and jlundin
    {IMPORTANT! This needs to be an array of empty values with the same number of values as the number of aliases in AliasArray.}
    
    bool Property TraceAliasRefs auto
    {Debug: default = false, if true will debug.trace() contents of all the aliases in the array after ForceRefInto() or ClearRefFrom()}
    
    bool threadLock
    
    Function LockThread()
        while threadLock
            utility.wait(1)
        endWhile
    
        threadLock = true
    EndFunction
    
    Function UnlockThread()
        threadLock = false
    EndFunction
    
    bool Function ForceRefInto(objectReference RefToAdd)
        
        LockThread()
    
        bool ReturnBool
        int foundRefIndex = RefArray.find(RefToAdd)
    
        if  foundRefIndex >= 0
            Debug.Trace(self + "ForceRefInto([" + RefToAdd + "]) is already in alias [" + AliasArray[foundRefIndex] + "], so NOT forcing into anything and will RETURN FALSE.")
            returnBool = false
    
        else
                    
            objectReference RefToReplace = PromoteRefs()
            ReferenceAlias AliasToReplace = FindRef(RefToReplace)
            
            Debug.Trace(self + "ForceRefInto([" + RefToAdd + "]) will and force reference into " + AliasToReplace)
    
            RefArray[0] = RefToAdd
            AliasToReplace.ForceRefTo(RefToAdd)
            returnBool = true
    
        endif
    
        if TraceAliasRefs
            TraceArrayRefs()
        endif
    
        UnlockThread()
    
        return returnBool
    
    EndFunction
    
    bool Function ClearRefFrom(objectReference RefToClear)
    
        LockThread()
    
        bool ReturnBool
        ReferenceAlias FoundAlias = FindRef(RefToClear)
    
        if FoundAlias
            Debug.Trace(self + "ClearRefFrom([" + RefToClear + "]) found Reference in Alias [" + FoundAlias + "], so will clear alias and RETURN TRUE")
            FoundAlias.Clear()
            RefArray[RefArray.Find(RefToClear)] = None                  ; !!! PAPYRUS !!!
            returnBool = true
    
        else
            Debug.Trace(self + "ClearRefFrom([" + RefToClear + "]) did NOT find reference in any aliases, will NOT clear anything, and will RETURN FALSE")
            returnBool = false
        endif
    
        if TraceAliasRefs
            TraceArrayRefs()
        endif
    
        UnlockThread()
        return returnBool
    
    
    EndFunction
    
    ReferenceAlias Function FindRef(objectReference RefToFind)
        debug.Trace(self + "FindRef([" + RefToFind + "])")
    
        int index = 0
        bool found = false
    
        ReferenceAlias AliasToReturn
    
           while (index < AliasArray.Length) && found == false
    
            if AliasArray[index].GetReference() == RefToFind
                AliasToReturn = AliasArray[index]
                found = true
    
            endif
    
            index += 1
    
           endWhile
    
           Debug.Trace(self + "FindRef([" + RefToFind + "]) found in alias " + AliasToReturn)
    
           RETURN AliasToReturn
               
    EndFunction
    
    objectReference Function PromoteRefs()
        Debug.Trace(self + "PromoteRefs()")
        int index = RefArray.Length - 1
        objectReference RefToReturn = RefArray[index]
    
           while (index >  0)
    
               objectReference currentRef = RefArray[index - 1]
    
               if currentRef
                RefArray[index] = (currentRef)
            endif
                    
            index -= 1
    
        endWhile
    
        return RefToReturn
    
    EndFunction
    
    function TraceArrayRefs()
        int index = 0
    
           while (index < AliasArray.Length)
               debug.trace("Alias: " + AliasArray[Index] + " has reference: " + AliasArray[Index].GetReference())
            index += 1
           endWhile
    
    EndFunction

     

     

     

    DLC1ReferenceAliasArrayScript (Usleep version)

     

    Scriptname DLC1ReferenceAliasArrayScript extends Quest  
    
    ;jduvall -- please talk to me before changing this script
    
    ReferenceAlias[] Property AliasArray auto
    {All the arrays that make up the "array"}
    
    objectReference[] Property RefArray auto ;we are using a seperate array to hold references and shuffling refs around in that because its much less overhead than shuffling refs into and out of aliases - requested by mlipari and jlundin
    {IMPORTANT! This needs to be an array of empty values with the same number of values as the number of aliases in AliasArray.}
    
    bool Property TraceAliasRefs auto
    {Debug: default = false, if true will debug.trace() contents of all the aliases in the array after ForceRefInto() or ClearRefFrom()}
    
    bool threadLock
    
    Function LockThread()
        while threadLock
            utility.wait(1)
        endWhile
    
        threadLock = true
    EndFunction
    
    Function UnlockThread()
        threadLock = false
    EndFunction
    
    bool Function ForceRefInto(objectReference RefToAdd)
        
        LockThread()
    
        bool ReturnBool
        int foundRefIndex = RefArray.find(RefToAdd)
    
        if  foundRefIndex >= 0
            ;Debug.Trace(self + "ForceRefInto([" + RefToAdd + "]) is already in alias [" + AliasArray[foundRefIndex] + "], so NOT forcing into anything and will RETURN FALSE.")
            returnBool = false
    
        else
                    
            objectReference RefToReplace = PromoteRefs()
            ReferenceAlias AliasToReplace = FindRef(RefToReplace)
            
            ;Debug.Trace(self + "ForceRefInto([" + RefToAdd + "]) will and force reference into " + AliasToReplace)
    
            RefArray[0] = RefToAdd
            AliasToReplace.ForceRefTo(RefToAdd)
            returnBool = true
    
        endif
    
        if TraceAliasRefs
            TraceArrayRefs()
        endif
    
        UnlockThread()
    
        return returnBool
    
    EndFunction
    
    bool Function ClearRefFrom(objectReference RefToClear)
    
        LockThread()
    
        bool ReturnBool
        ReferenceAlias FoundAlias = FindRef(RefToClear)
    
        if FoundAlias
            ;Debug.Trace(self + "ClearRefFrom([" + RefToClear + "]) found Reference in Alias [" + FoundAlias + "], so will clear alias and RETURN TRUE")
            FoundAlias.Clear()
            RefArray[RefArray.Find(RefToClear)] = None           ; !!! PAPYRUS !!!
            returnBool = true
    
        else
            ;Debug.Trace(self + "ClearRefFrom([" + RefToClear + "]) did NOT find reference in any aliases, will NOT clear anything, and will RETURN FALSE")
            returnBool = false
        endif
    
        if TraceAliasRefs
            TraceArrayRefs()
        endif
    
        UnlockThread()
        return returnBool
    
    
    EndFunction
    
    ReferenceAlias Function FindRef(objectReference RefToFind)
        ;debug.Trace(self + "FindRef([" + RefToFind + "])")
    
        int index = 0
        bool found = false
    
        ReferenceAlias AliasToReturn
    
           while (index < AliasArray.Length) && found == false
    
            if AliasArray[index].GetReference() == RefToFind
                AliasToReturn = AliasArray[index]
                found = true
    
            endif
    
            index += 1
    
           endWhile
    
           ;Debug.Trace(self + "FindRef([" + RefToFind + "]) found in alias " + AliasToReturn)
    
           RETURN AliasToReturn
               
    EndFunction
    
    objectReference Function PromoteRefs()
        ;Debug.Trace(self + "PromoteRefs()")
        int index = RefArray.Length - 1
        objectReference RefToReturn = RefArray[index]
    
           while (index >  0)
    
               objectReference currentRef = RefArray[index - 1]
    
               if currentRef
                RefArray[index] = (currentRef)
            endif
                    
            index -= 1
    
        endWhile
    
        return RefToReturn
    
    EndFunction
    
    function TraceArrayRefs()
        int index = 0
    
           while (index < AliasArray.Length)
               ;debug.trace("Alias: " + AliasArray[Index] + " has reference: " + AliasArray[Index].GetReference())
            index += 1
           endWhile
    
    EndFunction

     

     

     

    DLC1ReferenceAliasArrayScript (subhuman version)

     

    Scriptname DLC1ReferenceAliasArrayScript extends Quest  
    
    ;jduvall -- please talk to me before changing this script
    
    ReferenceAlias[] Property AliasArray auto
    {All the arrays that make up the "array"}
    
    objectReference[] Property RefArray auto ;we are using a seperate array to hold references and shuffling refs around in that because its much less overhead than shuffling refs into and out of aliases - requested by mlipari and jlundin
    {IMPORTANT! This needs to be an array of empty values with the same number of values as the number of aliases in AliasArray.}
    
    bool Property TraceAliasRefs auto
    {Debug: default = false, if true will debug.trace() contents of all the aliases in the array after ForceRefInto() or ClearRefFrom()}
    
    bool threadLock
    
    Function LockThread()
        while threadLock
    ; VSmO subhuman - 1 second is far too long to wait for array accesses, when papyrus can do thousands per second
    ;        utility.wait(1)
            utility.wait(0.1)
        endWhile
    
        threadLock = true
    EndFunction
    
    Function UnlockThread()
        threadLock = false
    EndFunction
    
    bool Function ForceRefInto(objectReference RefToAdd)
        
        LockThread()
    
        bool ReturnBool
        int foundRefIndex = RefArray.find(RefToAdd)
    
        if  foundRefIndex >= 0
            ;Debug.Trace(self + "ForceRefInto([" + RefToAdd + "]) is already in alias [" + AliasArray[foundRefIndex] + "], so NOT forcing into anything and will RETURN FALSE.")
            returnBool = false
    
        else
                    
            objectReference RefToReplace = PromoteRefs()
            ReferenceAlias AliasToReplace = FindRef(RefToReplace)
            
            ;Debug.Trace(self + "ForceRefInto([" + RefToAdd + "]) will and force reference into " + AliasToReplace)
    
            RefArray[0] = RefToAdd
            AliasToReplace.ForceRefTo(RefToAdd)
            returnBool = true
    
        endif
    
        if TraceAliasRefs
            TraceArrayRefs()
        endif
    
        UnlockThread()
    
        return returnBool
    
    EndFunction
    
    bool Function ClearRefFrom(objectReference RefToClear)
    
        LockThread()
    
        bool ReturnBool
        ReferenceAlias FoundAlias = FindRef(RefToClear)
    
        if FoundAlias
            ;Debug.Trace(self + "ClearRefFrom([" + RefToClear + "]) found Reference in Alias [" + FoundAlias + "], so will clear alias and RETURN TRUE")
            FoundAlias.Clear()
            RefArray[RefArray.Find(RefToClear)] = None
            returnBool = true
    
        else
            ;Debug.Trace(self + "ClearRefFrom([" + RefToClear + "]) did NOT find reference in any aliases, will NOT clear anything, and will RETURN FALSE")
            returnBool = false
        endif
    
        if TraceAliasRefs
            TraceArrayRefs()
        endif
    
        UnlockThread()
        return returnBool
    
    
    EndFunction
    
    ReferenceAlias Function FindRef(objectReference RefToFind)
        ;debug.Trace(self + "FindRef([" + RefToFind + "])")
    
    ; VSmO subhuman
    
        int index = 0
        while index < AliasArray.Length
            if AliasArray[index].GetReference() == RefToFind
                return AliasArray[index]
            endIf
            index += 1
        endWhile
        return none
    ;/    int index = 0
        bool found = false
    
        ReferenceAlias AliasToReturn
    
           while (index < AliasArray.Length) && found == false
    
            if AliasArray[index].GetReference() == RefToFind
                AliasToReturn = AliasArray[index]
                found = true
    
            endif
    
            index += 1
    
           endWhile
    
           ;Debug.Trace(self + "FindRef([" + RefToFind + "]) found in alias " + AliasToReturn)
    
           RETURN AliasToReturn/;
               
    EndFunction
    
    objectReference Function PromoteRefs()
        ;Debug.Trace(self + "PromoteRefs()")
        int index = RefArray.Length - 1
        objectReference RefToReturn = RefArray[index]
    
           while (index >  0)
    
               objectReference currentRef = RefArray[index - 1]
    
               if currentRef
                RefArray[index] = (currentRef)
            endif
                    
            index -= 1
    
        endWhile
    
        return RefToReturn
    
    EndFunction
    
    function TraceArrayRefs()
    ;/    int index = 0
    
           while (index < AliasArray.Length)
               ;debug.trace("Alias: " + AliasArray[Index] + " has reference: " + AliasArray[Index].GetReference())
            index += 1
           endWhile/;
    
    EndFunction

     

     

  18. https://www.creationkit.com/index.php?title=PlayAnimation_-_ObjectReference

     

    You wrote: "I ask for help from those who understand scripts and can write a simple script, the animation on IdleMarker should play for example (1 minute), then the character accepts its standard Idle animation."

     

    Its not as simple as you think. You have to save the actor who is doing the animation related to IdleMarker. Now the script has to wait for each actor 60 seconds and then a stop animation has to be called to revert the actor to normal behavior. Best way would be to create a new spell (fire and forget) with a script on magic effect.

     

    lis8IdleMarkerMGEFScript

     

    Scriptname lis8IdleMarkerMGEFScript extends ActiveMagicEffect
    ; https://forums.nexusmods.com/index.php?/topic/10943138-need-help-with-script-for-idlemarker-in-skyrim/
    
      Idle PROPERTY IdleStop_Loose auto        ; vanilla animation fill in here
    
      Actor target
    
    
    ; -- EVENTs --
    
    EVENT OnEffectStart(Actor akTarget, Actor akCaster)
        target = akTarget
        myF_Action(akTarget)
    ENDEVENT
    
    
    ; -- FUNCTION --
    
    ;----------------------------------
    FUNCTION myF_Action(Actor akTarget)
    ;----------------------------------
    int i = 60
        WHILE akTarget.Is3DLoaded()
            IF (i == 0)
                                                                                          ;* -- edited next code lines
    ;;;         (akTarget as ObjectReference).PlayAnimation("IdleStop_Loose")             ;* stop animation, this line is rubbish here
    ;;;         Debug.SendAnimationEvent((akTarget as ObjectReference), "IdleStop_Loose") ;* idle variant to play for actors without a property setting
                akTarget.PlayIdle(IdleStop_Loose)                                         ;* -- caused by earlier brain misfunction
    
                RETURN ; - STOP -
            ENDIF
    ;        ---------------------
            i = i - 1                                        ; counter
            Utility.Wait(1.0)
        ENDWHILE
    
        target = None            ; remove actors script persistence
    ENDFUNCTION

     


    What is the problem? I do not know how to cast the spell on the actor. You do not tell us with your post, what have you done to make the actor to interact with the idlemarker.

    In the same interaction you should cast the spell on the actor.

  19. You wrote: "I want to create a script based magic effect, and then be able to access that script effect so I can call functions from it."

     

    Depends on:

    1. count of actors be involved by this kind of magic effect

    2. content of function code to run here

    there are various ways to come along with.

     

    A simple way would be the use of a GlobalVariable with different values, which makes the magic effect to do somethings depends on value.

    A script of type activemagiceffect do not have "really" a self on it! So there is no way to call functions here from other scripts like external function call, as well as property get or set.

    But you can use OnUpdate() to wait for and respond of the value from a GlobalVariable. In other scripts you set the value to force action in the base magic effect.

     

    zorakConstantMGEFScript

     

    Scriptname zorakConstantMGEFScript extends ActiveMagicEffect
    ; https://forums.nexusmods.com/index.php?/topic/10939083-getting-a-reference-for-an-active-magic-effect-on-an-actor/
    
      GlobalVariable PROPERTY myGlobal auto        ; new created by CK
    
      Actor target
    
    
    ; -- EVENTs -- 4
    
    EVENT OnUpdate()
        myF_Action()
    ENDEVENT
    
    
    EVENT OnEffectStart(Actor akTarget, Actor akCaster)
        target = akTarget
        RegisterForSingleUpdate(1.0)
    ENDEVENT
    
    EVENT OnEffectFinish(Actor akTarget, Actor akCaster)
        target = None
    ENDEVENT
    
    
    EVENT OnDying(Actor akKiller)
        UnRegisterForUpdate()
        myGlobal.SetValue(-1.0)
        self.Dispel()
    ENDEVENT
    
    
    ; -- FUNCTIONs --
    
    ;--------------------
    FUNCTION myF_Action()
    ;--------------------
    IF ( target )
    ELSE
        self.Dispel()
        RETURN    ; - STOP -    target not found!
    ENDIF
    ;---------------------
    IF target.IsDead()
        RETURN    ; - STOP -    safety net..
    ENDIF
    ;---------------------
        float f = 1.0                        ; 1.0 - default cooldown time
        int i = myGlobalGetValueInt()
    
    IF ( i )                ; (i != 0)
        IF     (i == 1)
            myF_A01()
    
        ELSEIF (i == 2)
            myF_A02()
            f = 2.0                          ; 2.0 update default cooldown (as a sample here)
    
        ELSEIF (i == 3)
            myF_A03()
        ENDIF
    ENDIF
    
        IF (i >= 0)
            RegisterForSingleUpdate(f)       ; wait a bit before next action will be called
        ENDIF
    ENDFUNCTION
    
    
    ;-----------------
    FUNCTION myF_A01()
    ;-----------------
        ; code here
    ENDFUNCTION
    
    
    ;-----------------
    FUNCTION myF_A02()
    ;-----------------
        ; code here
    ENDFUNCTION
    
    
    ;-----------------
    FUNCTION myF_A03()
    ;-----------------
        ; code here
    ENDFUNCTION

     

     

  20. mod: "Lawbringer" v1.3.0.2 by EpicCrab https://www.nexusmods.com/skyrimspecialedition/mods/29882

     

    MLQ_LCOQuestScript

     

    Scriptname MLQ_LCOQuestScript extends Quest  
    
    Keyword Property CurrentOwnership Auto
    Keyword Property DefaultOwnership Auto
    ReferenceAlias[] Property ControlMarkerAliases Auto
    FormList Property RadiantExclusionFormList Auto
    
    Event onStoryScript(Keyword akKeyword, Location akLocation, ObjectReference akRef1, ObjectReference akRef2, int newController, int excludeFromRadiant)
        bool isSetupProperly = akLocation.hasKeyword(CurrentOwnership)
        if(newController == 0 && akLocation.hasKeyword(DefaultOwnership)) ;defaulting ownership and a specific faction is the default
            newController = akLocation.getKeywordData(DefaultOwnership) as int
        endIf
        if(isSetupProperly)
            int index = akLocation.getKeywordData(CurrentOwnership) as int
            ObjectReference currentControllerRef = ControlMarkerAliases[index].getReference()
            ObjectReference newControllerRef = ControlMarkerAliases[newController].getReference()
            if(currentControllerRef && !currentControllerRef.isDisabled() && newControllerRef && newControllerRef.isDisabled()) ;if any of these conditions aren't met it's best to just not deal with this
                currentControllerRef.disable()
                newControllerRef.enable()
                akLocation.setKeywordData(CurrentOwnership, newController as float)
                if(excludeFromRadiant == 0 && RadiantExclusionFormList.hasForm(akLocation))
                    RadiantExclusionFormList.removeAddedForm(akLocation)
                elseif(excludeFromRadiant != 0 && !RadiantExclusionFormList.hasForm(akLocation))
                    RadiantExclusionFormList.addForm(akLocation)
                endIf
            endIf
        else ; f*** you this is going to be so much messier now
            int index = ControlMarkerAliases.length
            ObjectReference currentControllerRef = None
            while (index > 0 && !currentControllerRef)
                index -= 1
                ObjectReference temp = ControlMarkerAliases[index].getReference()
                if(temp && !temp.isDisabled())
                    currentControllerRef = temp
                    ObjectReference newControllerRef = ControlMarkerAliases[newController].getReference()
                    if(currentControllerRef && !currentControllerRef.isDisabled() && newControllerRef && newControllerRef.isDisabled())
                        currentControllerRef.disable()
                        newControllerRef.enable()
                        if(excludeFromRadiant == 0 && RadiantExclusionFormList.hasForm(akLocation))
                            RadiantExclusionFormList.removeAddedForm(akLocation)
                        elseif(excludeFromRadiant != 0 && !RadiantExclusionFormList.hasForm(akLocation))
                            RadiantExclusionFormList.addForm(akLocation)
                        endIf
                    endIf
                endIf
            endWhile
        endIf
        stop()
    endEvent
    
    int function getControllerIndex(String controllerName) global
      ; ... code here ...
    endFunction

     

     

     

    Without a specific error line its impossible to say what kind of code is throwing the errors. So I have the script partly rewritten.

    If you want to compile next source keep in mind I do not added the single function from original script. Maybe the mod needs this function getControllerIndex() to work properly.

    MLQ_LCOQuestScript

     

    Scriptname MLQ_LCOQuestScript extends Quest
    {adjusted by ReDragon 2022-01-01}
    ; https://forums.nexusmods.com/index.php?/topic/10898648-papyrus-log-has-infinitely-repeating-error-that-i-cant-find-from-base-game-or-from-a-mod/
    
      Keyword PROPERTY DefaultOwnership auto
      Keyword PROPERTY CurrentOwnership auto
    
      FormList PROPERTY RadiantExclusionFormList auto            ; radiant exclude list
    
      ReferenceAlias[] PROPERTY ControlMarkerAliases auto        ; pre-filled array of marker aliases
    
      Bool bBusy    ; [default=False]
    
    
    ; -- EVENT --
    
    EVENT OnStoryScript(Keyword akKeyword, Location akLocation, ObjectReference akRef1, ObjectReference akRef2, Int aiValue1, Int aiValue2)
    ;====================================
    ; https://www.creationkit.com/index.php?title=OnStoryScript_-_Quest
    ; aiValue1 = newController
    ; aiValue2 = excludeFromRadiant
    
    IF ( bBusy )
        RETURN    ; - STOP -    Do not allow multiple event calls to stop this quest!
    ENDIF
    ;---------------------
        bBusy = TRUE            ; *T*
    
        myF_Action(akLocation, aiValue1, aiValue2, akLocation.HasKeyword(CurrentOwnership))
    
    ;;;    bBusy = False            ; ***
        self.Stop()        ; stop this quest
    ENDEVENT
    
    
    ; -- FUNCTIONs -- 3
    
    ;----------------------------------------------------
    FUNCTION myF_Debug(Int n, Location akLocation, Int i)  ; helper to shrink stack size
    ;----------------------------------------------------
    IF (n == 0)
        Debug.Trace(self+" OnStoryScript("+i+") - ERROR-0: Location is <None> .. " +akLocation)
        RETURN    ; - STOP -
    ENDIF
    ;---------------------
    IF (n == 1)
        Debug.Trace(self+" OnStoryScript("+i+") - ERROR-1: Location = " +akLocation+ " has invalid KeywordData!")
        RETURN    ; - STOP -
    ENDIF
    ;---------------------
    IF (n == 2)
        Debug.Trace(self+" OnStoryScript("+i+") - ERROR-2: Location = " +akLocation+ " detected invalid newController value!")
        RETURN    ; - STOP -
    ENDIF
    ;---------------------
    IF (n == 3)
        Debug.Trace(self+" OnStoryScript("+i+") - ERROR-3: Location = " +akLocation+ " detected invalid newController value!")
    ENDIF
    ENDFUNCTION
    
    
    ;-----------------------------------------------------
    FUNCTION myF_AddRem(Location akLocation, Int aiValue2)  ; internal helper
    ;-----------------------------------------------------
    IF ( akLocation )
    ELSE
        RETURN    ; - STOP -    safety net
    ENDIF
    ;---------------------
        int i = RadiantExclusionFormList.Find( akLocation )
        IF ( aiValue2 )                                                        ; (excludeFromRadiant != 0)
            IF (i < 0)
                RadiantExclusionFormList.AddForm(akLocation)
            ENDIF
        ELSE
            IF (i >= 0)
                RadiantExclusionFormList.RemoveAddedForm(akLocation)
            ENDIF
        ENDIF
    ENDFUNCTION
    
    
    ;-----------------------------------------------------------------------------
    FUNCTION myF_Action(Location akLocation, Int aiValue1, Int aiValue2, Bool bOK)  ; code outsourced from event
    ;-----------------------------------------------------------------------------
    IF ( akLocation )
    ELSE
        myF_Debug(0, akLocation, aiValue1)                        ; //DEBUG-0
        RETURN    ; - STOP - /1
    ENDIF
    ;---------------------
        IF (aiValue1 == 0) && akLocation.HasKeyword(DefaultOwnership)        ; default ownership and specific faction is default
            aiValue1 = akLocation.GetKeywordData(DefaultOwnership) as Int
        ENDIF
    
        objectReference currentControllerRef
        objectReference newRef                                               ; newRef = newControllerRef
        int i                                                                ; i = index
    
    ;---------------------
    IF ( bOK )                                                               ; bOK = isSetupProperly
        i = akLocation.GetKeywordData(CurrentOwnership) as Int
    
        IF (i < ControlMarkerAliases.Length)
            currentControllerRef = ControlMarkerAliases[i].GetReference()            ; - CURRENT -
        ELSE
            myF_Debug(1, akLocation, i)                           ; //DEBUG-1
        ENDIF
    ;    -------------------------------------------
        IF (aiValue1 < ControlMarkerAliases.Length)
            newRef = ControlMarkerAliases[aiValue1].GetReference()                    ; - NEW -
        ELSE
            myF_Debug(2, akLocation, aiValue1)                    ; //DEBUG-2
        ENDIF
    
        ; if any of these conditions aren't met it's best to just not deal with this
    
        IF (currentControllerRef) && !currentControllerRef.IsDisabled()
        ELSE
            RETURN    ; - STOP - invalid REF /or/ controller is disabled
        ENDIF
    ;    ----------------------
        IF (newRef) && newRef.IsDisabled()
            currentControllerRef.Disable()
            newRef.Enable()
    
            akLocation.SetKeywordData(CurrentOwnership, aiValue1 as Float)
            myF_AddRem(akLocation, aiValue2)
        ENDIF
        RETURN    ; - STOP -    isSetupProperly == TRUE
    ENDIF
    ;---------------------
    ; f*** you this is going to be so much messier now
    
        i = ControlMarkerAliases.Length
        WHILE (i > 0)
            i = i - 1
            currentControllerRef = ControlMarkerAliases[i].GetReference()    ; temp
    
            IF (currentControllerRef) && !currentControllerRef.IsDisabled()
                IF (aiValue1 >= ControlMarkerAliases.Length)
                    currentControllerRef = None
                    myF_Debug(3, akLocation, aiValue1)            ; //DEBUG-3
                ELSE
                    newRef = ControlMarkerAliases[aiValue1].GetReference()
                ENDIF
    
                IF (newRef) && newRef.IsDisabled()
                    currentControllerRef.Disable()
                    newRef.Enable()
    
    ;?;             akLocation.SetKeywordData(CurrentOwnership, aiValue1 as Float)        ; code line not found in original script !!!
                    myF_AddRem(akLocation, aiValue2)
                    RETURN ; - STOP -
                ENDIF
    ;           ---------------------
                currentControllerRef = None
                newRef               = None
            ENDIF
        ENDWHILE
    ENDFUNCTION

     

     

  21. Keep in mind EquipItem() is for actors only. Your script is extending to type ObjectReference, that could be the reason why you are adding/using this: Actor Property MySelf Auto

     

    avid_FollowerEquipScript

     

    Scriptname avid_FollowerEquipScript extends ObjectReference
    ; https://forums.nexusmods.com/index.php?/topic/10902633-equipitem-does-not-equip-item/
    
    ; baseobject of leveled items
      LeveledItem PROPERTY ArmorBody auto    ; body
      LeveledItem PROPERTY ArmorHand auto    ; gauntlet, gloves
      LeveledItem PROPERTY ArmorFeet auto    ; boots
    
    
    ; -- EVENTs --
    
    EVENT OnInit()
        gotoState("GiveItems")            ; ### STATE ###
        RegisterForSingleUpdateGameTime(0.0)
    ENDEVENT
    
    EVENT OnLoad()
        myF_EquipMe()   ; follower has 3D loaded equip the items
    ENDEVENT
    
    ;========================================
    State GiveItems
    ;==============
        EVENT OnItemAdded(Form akBaseItem, Int aiItemCount, ObjectReference akItemReference, ObjectReference akSourceContainer)
            IF (aiItemCount == 1)
                myF_Add(akBaseItem, akSourceContainer)
            ENDIF
        ENDEVENT
    
        EVENT OnUpdateGameTime()
            Utility.Wait(0.1)
            InitArray()
    
    ;        ; ***************************************************
            ; We assume this script runs on the follower himself!
            ; ***************************************************
    
            self.AddItem(ArmorBody, 1, TRUE)
            Debug.Trace(self+" Body = " +ArmorBody)          ; debug
        
            self.AddItem(ArmorHand, 1, TRUE)
            Debug.Trace(self+" gloves = " +ArmorHand)        ; debug
    
            self.AddItem(ArmorFeet, 1, TRUE)
            Debug.Trace(self+" boots = " +ArmorFeet)         ; debug
        ENDEVENT
    ;=======
    endState
    
    
    ; -- FUNCTIONs -- 4
    
      Form[] PROPERTY ArmorArray auto Hidden
      Int    itemCount  ; [default=0]
    
    ;-------------------
    FUNCTION InitArray()
    ;-------------------
        ArmorArray = new Form[3]
        itemCount = 0              ; reset the counter as well
    ENDFUNCTION
    
    ;----------------------
    FUNCTION DestroyArray()
    ;----------------------
        form[] b
        ArmorArray = b
    ENDFUNCTION
    
    
    ;-------------------------------------------------------------------
    FUNCTION myF_Add(Form akBaseItem, ObjectReference akSourceContainer)
    ;-------------------------------------------------------------------
    IF ( akSourceContainer )
        RETURN    ; - STOP -    item was given by actor (player as well) /or/ taken from container
    ENDIF
    ;---------------------
        itemCount += 1           ; increase by 1
            
    IF (itemCount == 1)
        ArmorArray[0] = akBaseItem
        RETURN    ; - STOP -
    ENDIF
    ;---------------------
    IF (itemCount == 2)
        ArmorArray[1] = akBaseItem
        RETURN    ; - STOP -
    ENDIF
    ;---------------------
    IF (itemCount == 3)
        gotoState("")                ; ### STATE ###    got all three items
        ArmorArray[2] = akBaseItem
        IF self.Is3DLoaded()
            myF_EquipMe()
        ENDIF
    ENDIF
    ENDFUNCTION
    
    
    ;---------------------
    FUNCTION myF_EquipMe()
    ;---------------------
        actor aRef = (self as ObjectReference) as Actor        ; EquipItem() is exclusive for Actors and needs 3D loaded
    
    IF (ArmorArray.Length == 3)
        aRef.EquipItem( ArmorArray[0] )
        aRef.EquipItem( ArmorArray[1] )
        aRef.EquipItem( ArmorArray[2] )
    ENDIF
    ENDFUNCTION

     

     

  22. You wrote: "any thoughts would be welcome"

    I think you should split your function in subfunctions to get a better overview. In my opinion the "Adventurer_Array" is wrong initialized!

     

    maybe next code is useful to you

     

    ; https://forums.nexusmods.com/index.php?/topic/10894493-form-list-comparison-question/
    
      Faction PROPERTY PotentialFollowerFaction auto
      Faction PROPERTY DTPotentialFollower      auto
    
      FormList PROPERTY DTFollowersToExcludeList auto
    
      Int PROPERTY NPC_Form_Type auto        ; set to 0x??
    
    ;------------------------------
    FUNCTION Get_Adventurer_Array()
    ;------------------------------
        int[] b = new Int[1]                                     ; b = Base_Form_Types
        b[0] = NPC_Form_Type
        
        objectReference[] a = SkyPal_References.All()            ;* a = NPC_Array
        Debug.MessageBox("NPCs found " +a.Length)
    
        a = SkyPal_References.Filter_Form_Types(NPC_Array, b)    ;* limit array to NPC_Form_Type
        Debug.MessageBox("Characters found " + a.Length)
    
        myF_DestroyArray()
    ;;;    Adventurer_Array = new Actor[128]        ; !!!
        
        int i = 0                                                ; i = Index
        WHILE (i < a.Length)
    
            actor aRef = a[i] as Actor                           ; aRef = ref_to_check
            IF ( aRef )
                IF aRef.IsInFaction(DTPotentialFollower)
                    ; already in this faction
    
                ELSEIF DTFollowersToExcludeList.HasForm(aRef as Form)            ; *** suggested by Ghaunadaur ***
                    ; actor found in exclude list
    
    ;?;         ELSEIF DTFollowersToExcludeList.HasForm( aRef.GetBaseObject() )  ; depends on: What is the content of your formlist?, this could also work
                    ; actorbase found
    
                ELSEIF aRef.IsInFaction(PotentialFollowerFaction)
                    myF_ADD(aRef, Utility.RandomInt(0, 100))
                ENDIF
            ENDIF
    
            i = i + 1
        ENDWHILE
    
        Debug.MessageBox("Adventurers found " +Adventurer_Array.Length)
    ENDFUNCTION
    
    
      Actor[] PROPERTY Adventurer_Array auto Hidden        ; property here to get access from other scripts
    
    ;--------------------------
    FUNCTION myF_DestroyArray()
    ;--------------------------
        actor[] b
        Adventurer_Array = b
    ENDFUNCTION
    
    ;-----------------------
    FUNCTION myF_InitArray()
    ;-----------------------
        Adventurer_Array = new Actor[128]
    ENDFUNCTION
    
    ;----------------------------------
    FUNCTION myF_ADD(Actor aRef, Int r)
    ;----------------------------------
        ArrayAddRef(Adventurer_Array, aRef)                        ;** no idea what function is that??
    
        aRef.RemoveFromFaction(PotentialFollowerFaction)
        aRef.AddToFaction(DTPotentialFollower)
    
        aRef.SetRelationshipRank(Game.GetPlayer(), 0)
        aRef.SetFactionRank(DTPotentialFollower, r)                ;  valid range: -128 .. 127
    ENDFUNCTION

     

     

×
×
  • Create New...