Jump to content

[LE] Code optimization questions


Recommended Posts

jacobpaige wrote: "For reference, here's the current version of the code (still need to add the SKSE check):"

 

Scriptname EquipmentSwappingScript extends activemagiceffect  
{Swaps equipment between NPC and Player, if that NPC does not have a designated outfit.}

FormList Property playerGearForSwapping Auto

FormList Property npcGearForSwapping Auto

Actor Property PlayerREF Auto

Function GetWornEquipmentButNotHands(Actor akTarget, FormList akWornForms)
{adapted from https://www.creationkit.com/index.php?title=Slot_Masks_-_Armor example}
    int index
    int slotsChecked
    slotsChecked += 0x00100000
    slotsChecked += 0x00200000 ;ignore reserved slots
    slotsChecked += 0x80000000
 
    int thisSlot = 0x01
    while (thisSlot < 0x80000000)
        if (Math.LogicalAnd(slotsChecked, thisSlot) != thisSlot) ;only check slots we have not found anything equipped on already
            Armor thisArmor = akTarget.GetWornForm(thisSlot) as Armor
            if (thisArmor)
                akWornForms.AddForm(thisArmor)
                index += 1
                slotsChecked += thisArmor.GetSlotMask() ;add all slots this item covers to our slotsChecked variable
            else ;no armor was found on this slot
                slotsChecked += thisSlot
            endif
        endif
        thisSlot *= 2 ;double the number to move on to the next slot
    endWhile
EndFunction

Function SwapEquipment(Actor akFirst, Actor akSecond, FormList akListOfItems)
    akFirst.RemoveItem(akListOfItems, 1, true, akSecond)
EndFunction

Function EquipPlayer()
    int idx = 0
    Form[] listOfItems = npcGearForSwapping.ToArray() ;EquipItemEx will not take FormLists
    while(idx < listOfItems.Length)
        PlayerREF.EquipItemEx(listOfItems[idx])
        idx += 1
    endWhile
EndFunction

Function FindAndSwapGear(Actor akNPC)
    GetWornEquipmentButNotHands(PlayerREF, playerGearForSwapping)
    GetWornEquipmentButNotHands(akNPC, npcGearForSwapping)
    
    SwapEquipment(PlayerREF, akNPC, playerGearForSwapping)
    SwapEquipment(akNPC, PlayerREF, npcGearForSwapping)
    
    EquipPlayer()
    
    ;cleanup
    playerGearForSwapping.Revert()
    npcGearForSwapping.Revert()
EndFunction

Event OnEffectStart(Actor akTarget, Actor akCaster)
    if (akCaster == PlayerREF && akTarget.GetActorBase().GetOutfit().GetNumParts() == 0)
    ;to prevent item duplication
        FindAndSwapGear(akTarget)
    elseif(akTarget == PlayerREF)
        FindAndSwapGear(akCaster)
    endif
EndEvent

 



Dear jacobpaige,

I find it offensive when someone who asks for help tries to teach the helpers. My English is not as good as yours, so I always try to take simple name convention that can understand everyone.
Maybe someday you will get into such a situation. Nevertheless you have answered and shown gratitude.

Here is the script optimized in runtime and stacksize usage, remember you gave us code snippets and wrote nothing about the mod purpose, that comes just slowly to the surface:

 

Scriptname jpSwapArmorEffectScript extends ActiveMagicEffect
{optimized by ReDragon 2018} ; swaps equipment between NPC and Player, if that NPC does not have a designated outfit.

; https://forums.nexusmods.com/index.php?/topic/6619756-code-optimization-questions/page-3
; jacobpaige wrote: "For reference, here's the current version of the code (still need to add the SKSE check)"

; "Thanks for all the suggestions by the way. I realize how much time it must have taken to write all that,
; and it's helpful to see the approaches that other people would have taken."

  FormList PROPERTY playerGearList auto Hidden        ; playerGearForSwapping
  FormList PROPERTY npcGearList    auto Hidden        ; npcGearForSwapping

  Int iBusy = 2        ; threadlock variable

; make both actors persistent for a while by using script variables
  Actor playerRef
  Actor npcRef

;-------------------------
Bool FUNCTION myF_IsSKSE()
;-------------------------
IF (SKSE.GetVersion() > 0)
    Return TRUE
ENDIF
;---------
    Debug.Trace("jpSwapArmor: SKSE is missing! " +self)        ; see "papyrus.0.log" within folder "..\My Games\Skyrim\Logs\Script"
    Return False
ENDFUNCTION

;---------------------------------------------------------
Bool FUNCTION myF_IsPlayer(Actor akTarget, Actor akCaster)
;---------------------------------------------------------
    playerRef = Game.GetPlayer()    ; fill script variable

IF (akTarget == playerRef)
    IF (akTarget == akCaster)
        Return False    ; player is both
    ENDIF
    npcRef = akCaster        ;    npc is the caster
    Return TRUE                ; * and player is target
ENDIF
;---------
IF (akCaster == playerRef)
    ; https://www.creationkit.com/index.php?title=GetNumParts_-_Outfit
    outfit OT = akTarget.GetActorBase().GetOutfit()
    IF (OT) && (OT.GetNumParts() == 0)            ; SKSE required! GetOutfit(), GetNumParts()
        npcRef = akTarget    ; npc ist the target
        Return TRUE            ; * and player is caster
    ENDIF
ENDIF
    Return False        ; player not found
ENDFUNCTION


; -- EVENTs -- 3

EVENT OnEffectStart(Actor akTarget, Actor akCaster)
IF myF_IsSKSE()
ELSE
    self.Dispel()
    RETURN    ; - STOP - SKSE not found
ENDIF
;---------------------
IF myF_IsPlayer(akTarget, akCaster)
ELSE
    self.Dispel()
    RETURN    ; - STOP - something is bad
ENDIF
;---------------------
    RegisterForSingleUpdate(0.0)            ; register additional thread
    RegisterForSingleUpdateGameTime(0.0)    ; register additional thread

;---
    WHILE (iBusy)                ; (iBusy > 0)
        Utility.Wait(0.1)        ; we are hold on here
    ENDWHILE                     ; until both update events are finished
;---
    SwapEquipments()
    EquipPlayer()

; cleanup script variables
    playerGearList.Revert()
    npcGearList.Revert()
ENDEVENT


EVENT OnUpdate()
    GetWornEquipmentByPlayer()
ENDEVENT

EVENT OnUpdateGameTime()
    GetWornEquipmentByNPC()
ENDEVENT


; -- FUNCTIONs -- (2) + 4 = 6

;------------------------
FUNCTION SwapEquipments()
;------------------------
    npcRef.RemoveItem(npcGearList as Form, 1, TRUE, playerRef as ObjectReference)     ; to the player
    Utility.Wait(0.05)
    playerRef.RemoveItem(playerGearList as Form, 1, TRUE, npcRef as ObjectReference)  ; to the npc
ENDFUNCTION


;---------------------
FUNCTION EquipPlayer()
;---------------------
;;;    form[] a = npcGearList.ToArray()    ; **OBSOLETE**

int iMax = npcGearList.GetSize()
int i = 0
    WHILE (i < iMax)
        playerRef.EquipItemEx( npcGearList.GetAt(i) )    ; SKSE !!
        i = i + 1
    ENDWHILE
ENDFUNCTION


;----------------------------------
FUNCTION GetWornEquipmentByPlayer()  ; Player action threading safe
;----------------------------------
; adapted from https://www.creationkit.com/index.php?title=Slot_Masks_-_Armor

;/ *** avoid next SKSE provided slotmasks
;   int Property kSlotMask50 = 0x00100000 AutoReadOnly ; DecapitateHead
;   int Property kSlotMask51 = 0x00200000 AutoReadOnly ; Decapitate
;   int Property kSlotMask61 = 0x80000000 AutoReadOnly ; FX01
****** /;

    int iMask = 0x80300000            ; ignore slotmasks by default
    int i     = 0x00000001            ; init by 1

WHILE (i < 0x80000000)
    IF (Math.LogicalAnd(iMask, i) == i)              ; SKSE !!
        ; slots we have found equipped on already or excluded by default
    ELSE
        form fm = playerRef.GetWornForm(iSlot)       ; SKSE !!
        IF (fm as Armor)
            playerGearList.AddForm(fm)
            iMask += fm.GetSlotMask()                ; SKSE !!    add all slots this item covers too
        ELSE
            iMask += i                ; no armor was found on this slot
        ENDIF
    ENDIF
    i = i * 2                         ; double the number to move on to the next slot
ENDWHILE
    myF_Ready()
ENDFUNCTION

;-------------------
FUNCTION myF_Ready()
;-------------------
    iBusy = iBusy - 1        ; update threadlock variable
ENDFUNCTION

;-------------------------------
FUNCTION GetWornEquipmentByNPC()  ; NPC action threading safe
;-------------------------------
; adapted from https://www.creationkit.com/index.php?title=Slot_Masks_-_Armor

;/ *** avoid next SKSE provided slotmasks
;   int Property kSlotMask50 = 0x00100000 AutoReadOnly ; DecapitateHead
;   int Property kSlotMask51 = 0x00200000 AutoReadOnly ; Decapitate
;   int Property kSlotMask61 = 0x80000000 AutoReadOnly ; FX01
****** /;

    int iMask = 0x80300000            ; ignore slotmasks by default
    int i     = 0x00000001            ; init by 1

WHILE (i < 0x80000000)
    IF (Math.LogicalAnd(iMask, i) == i)               ; SKSE !!
        ; slots we have found equipped on already or excluded by default
    ELSE
        form fm = npcRef.GetWornForm(iSlot)           ; SKSE !!
        IF (fm as Armor)
            npcGearList.AddForm(fm)
            iMask += fm.GetSlotMask()                 ; SKSE !!    add all slots this item covers too
        ELSE
            iMask += i                ; no armor was found on this slot
        ENDIF
    ENDIF
    i = i * 2                         ; double the number to move on to the next slot
ENDWHILE
    myF_Ready()
ENDFUNCTION


;/ *** SKSE implementation ********************************
int Property kSlotMask30 = 0x00000001 AutoReadOnly ; HEAD
int Property kSlotMask31 = 0x00000002 AutoReadOnly ; Hair
int Property kSlotMask32 = 0x00000004 AutoReadOnly ; BODY
int Property kSlotMask33 = 0x00000008 AutoReadOnly ; Hands
int Property kSlotMask34 = 0x00000010 AutoReadOnly ; Forearms
int Property kSlotMask35 = 0x00000020 AutoReadOnly ; Amulet
int Property kSlotMask36 = 0x00000040 AutoReadOnly ; Ring
int Property kSlotMask37 = 0x00000080 AutoReadOnly ; Feet
int Property kSlotMask38 = 0x00000100 AutoReadOnly ; Calves
int Property kSlotMask39 = 0x00000200 AutoReadOnly ; SHIELD
int Property kSlotMask40 = 0x00000400 AutoReadOnly ; TAIL
int Property kSlotMask41 = 0x00000800 AutoReadOnly ; LongHair
int Property kSlotMask42 = 0x00001000 AutoReadOnly ; Circlet
int Property kSlotMask43 = 0x00002000 AutoReadOnly ; Ears
int Property kSlotMask44 = 0x00004000 AutoReadOnly ; Unnamed
int Property kSlotMask45 = 0x00008000 AutoReadOnly ; Unnamed
int Property kSlotMask46 = 0x00010000 AutoReadOnly ; Unnamed
int Property kSlotMask47 = 0x00020000 AutoReadOnly ; Unnamed
int Property kSlotMask48 = 0x00040000 AutoReadOnly ; Unnamed
int Property kSlotMask49 = 0x00080000 AutoReadOnly ; Unnamed
int Property kSlotMask50 = 0x00100000 AutoReadOnly ; DecapitateHead  * masked out
int Property kSlotMask51 = 0x00200000 AutoReadOnly ; Decapitate      * masked out
int Property kSlotMask52 = 0x00400000 AutoReadOnly ; Unnamed
int Property kSlotMask53 = 0x00800000 AutoReadOnly ; Unnamed
int Property kSlotMask54 = 0x01000000 AutoReadOnly ; Unnamed
int Property kSlotMask55 = 0x02000000 AutoReadOnly ; Unnamed
int Property kSlotMask56 = 0x04000000 AutoReadOnly ; Unnamed
int Property kSlotMask57 = 0x08000000 AutoReadOnly ; Unnamed
int Property kSlotMask58 = 0x10000000 AutoReadOnly ; Unnamed
int Property kSlotMask59 = 0x20000000 AutoReadOnly ; Unnamed
int Property kSlotMask60 = 0x40000000 AutoReadOnly ; Unnamed
int Property kSlotMask61 = 0x80000000 AutoReadOnly ; FX01            * masked out
************************************************************ /;

 



Once more for the future, if you use script code or suggestions provided by other forum users,
its a good idea to say thank you within the source code, not only anonymous inside a forum thread
that will be forgotten next month.

Edited by ReDragon2013
Link to comment
Share on other sites

  • Replies 46
  • Created
  • Last Reply

Top Posters In This Topic

Dear jacobpaige,

 

I find it offensive when someone who asks for help tries to teach the helpers. My English is not as good as yours, so I always try to take simple name convention that can understand everyone.

Maybe someday you will get into such a situation. Nevertheless you have answered and shown gratitude.

 

Here is the script optimized in runtime and stacksize usage, remember you gave us code snippets and wrote nothing about the mod purpose, that comes just slowly to the surface:

 

Scriptname jpSwapArmorEffectScript extends ActiveMagicEffect
{optimized by ReDragon 2018} ; swaps equipment between NPC and Player, if that NPC does not have a designated outfit.

; https://forums.nexusmods.com/index.php?/topic/6619756-code-optimization-questions/page-3
; jacobpaige wrote: "For reference, here's the current version of the code (still need to add the SKSE check)"

; "Thanks for all the suggestions by the way. I realize how much time it must have taken to write all that,
; and it's helpful to see the approaches that other people would have taken."

  FormList PROPERTY playerGearList auto Hidden        ; playerGearForSwapping
  FormList PROPERTY npcGearList    auto Hidden        ; npcGearForSwapping

  Int iBusy = 2        ; threadlock variable

; make both actors persistent for a while by using script variables
  Actor playerRef
  Actor npcRef

;-------------------------
Bool FUNCTION myF_IsSKSE()
;-------------------------
IF (SKSE.GetVersion() > 0)
    Return TRUE
ENDIF
;---------
    Debug.Trace("jpSwapArmor: SKSE is missing! " +self)        ; see "papyrus.0.log" within folder "..\My Games\Skyrim\Logs\Script"
    Return False
ENDFUNCTION

;---------------------------------------------------------
Bool FUNCTION myF_IsPlayer(Actor akTarget, Actor akCaster)
;---------------------------------------------------------
    playerRef = Game.GetPlayer()

IF (akTarget == playerRef)
    IF (akTarget == akCaster)
        Return False    ; player is both
    ENDIF
    npcRef = akCaster        ;    npc is the caster
    Return TRUE                ; * and player is target
ENDIF
;---------
IF (akCaster == playerRef)
    ; https://www.creationkit.com/index.php?title=GetNumParts_-_Outfit
    IF (akTarget.GetActorBase().GetOutfit().GetNumParts() == 0)            ; SKSE required! GetOutfit(), GetNumParts()
        npcRef = akTarget    ; npc ist the target
        Return TRUE            ; * and player is caster
    ENDIF
ENDIF
    Return False        ; player not found
ENDFUNCTION


; -- EVENTs -- 3

EVENT OnEffectStart(Actor akTarget, Actor akCaster)
IF myF_IsSKSE()
ELSE
    self.Dispel()
    RETURN    ; - STOP - SKSE not found
ENDIF
;---------------------
IF myF_IsPlayer(akTarget, akCaster)
ELSE
    self.Dispel()
    RETURN    ; - STOP - something is bad or missing player
ENDIF
;---------------------
    RegisterForSingleUpdate(0.0)            ; create a second thread
    RegisterForSingleUpdateGameTime(0.0)    ; create a third thread

;---
    WHILE (iBusy)                ; (iBusy > 0)
        Utility.Wait(0.25)        ; we are hold on here
    ENDWHILE                    ; until both update events are finished
;---
    SwapEquipments()
    EquipPlayer()
    
    playerGearList.Revert()        ; cleanup both formlists
    npcGearList.Revert()
ENDEVENT


EVENT OnUpdate()
    GetWornEquipmentByPlayer()
ENDEVENT

EVENT OnUpdateGameTime()
    GetWornEquipmentByNPC()
ENDEVENT


; -- FUNCTIONs -- (2) + 4 = 6

;------------------------
FUNCTION SwapEquipments()
;------------------------
    playerRef.RemoveItem(playerGearList, 1, TRUE, npcRef as ObjectReference)
    npcRef.RemoveItem(npcGearList, 1, TRUE, playerRef as ObjectReference)
ENDFUNCTION


;---------------------
FUNCTION EquipPlayer()
;---------------------
;;;    form[] a = npcGearList.ToArray()    ; **OBSOLETE**

int iMax = npcGearList.GetSize()
int i = 0
    WHILE (i < iMax)
        playerRef.EquipItemEx( npcGearList.GetAt(i) )    ; SKSE !!
        i = i + 1
    ENDWHILE
ENDFUNCTION


;----------------------------------
FUNCTION GetWornEquipmentByPlayer()  ; Player action threading safe
;----------------------------------
; adapted from https://www.creationkit.com/index.php?title=Slot_Masks_-_Armor

;/ *** avoid next SKSE provided slotmasks
    int Property kSlotMask50 = 0x00100000 AutoReadOnly ; DecapitateHead
    int Property kSlotMask51 = 0x00200000 AutoReadOnly ; Decapitate
    int Property kSlotMask61 = 0x80000000 AutoReadOnly ; FX01
****** /;

    int iMask = 0x80300000            ; ignore slotmasks by default
    int iSlot = 0x00000001            ; init by 1
    int i

WHILE (iSlot < 0x80000000)
    IF (Math.LogicalAnd(iMask, iSlot) == iSlot)     ; SKSE !!
        ; slots we have found equipped on already or excluded by initial
    ELSE
        form fm = playerRef.GetWornForm(iSlot)        ; SKSE !!
        IF (fm as Armor)
            playerGearList.AddForm(fm)
            i = i + 1
            iMask += fm.GetSlotMask()                ; SKSE !!    add all slots this item covers too
        ELSE
            iMask += iSlot            ; no armor was found on this slot
        ENDIF
    ENDIF
    iSlot = iSlot * 2                 ; double the number to move on to the next slot
ENDWHILE

    iBusy = iBusy - 1
ENDFUNCTION


;-------------------------------
FUNCTION GetWornEquipmentByNPC()  ; NPC action threading safe
;-------------------------------
; adapted from https://www.creationkit.com/index.php?title=Slot_Masks_-_Armor

;/ *** avoid next SKSE provided slotmasks
    int Property kSlotMask50 = 0x00100000 AutoReadOnly ; DecapitateHead
    int Property kSlotMask51 = 0x00200000 AutoReadOnly ; Decapitate
    int Property kSlotMask61 = 0x80000000 AutoReadOnly ; FX01
****** /;

    int iMask = 0x80300000            ; ignore slotmasks by default
    int iSlot = 0x00000001            ; init by 1
    int i

WHILE (iSlot < 0x80000000)
    IF (Math.LogicalAnd(iMask, iSlot) == iSlot)     ; SKSE !!
        ; slots we have found equipped on already or excluded by initial
    ELSE
        form fm = npcRef.GetWornForm(iSlot)            ; SKSE !!
        IF (fm as Armor)
            npcGearList.AddForm(fm)
            i = i + 1
            iMask += fm.GetSlotMask()                ; SKSE !!    add all slots this item covers too
        ELSE
            iMask += iSlot            ; no armor was found on this slot
        ENDIF
    ENDIF
    iSlot = iSlot * 2                 ; double the number to move on to the next slot
ENDWHILE

    iBusy = iBusy - 1
ENDFUNCTION


;/ *** SKSE implementation ********************************
int Property kSlotMask30 = 0x00000001 AutoReadOnly ; HEAD
int Property kSlotMask31 = 0x00000002 AutoReadOnly ; Hair
int Property kSlotMask32 = 0x00000004 AutoReadOnly ; BODY
int Property kSlotMask33 = 0x00000008 AutoReadOnly ; Hands
int Property kSlotMask34 = 0x00000010 AutoReadOnly ; Forearms
int Property kSlotMask35 = 0x00000020 AutoReadOnly ; Amulet
int Property kSlotMask36 = 0x00000040 AutoReadOnly ; Ring
int Property kSlotMask37 = 0x00000080 AutoReadOnly ; Feet
int Property kSlotMask38 = 0x00000100 AutoReadOnly ; Calves
int Property kSlotMask39 = 0x00000200 AutoReadOnly ; SHIELD
int Property kSlotMask40 = 0x00000400 AutoReadOnly ; TAIL
int Property kSlotMask41 = 0x00000800 AutoReadOnly ; LongHair
int Property kSlotMask42 = 0x00001000 AutoReadOnly ; Circlet
int Property kSlotMask43 = 0x00002000 AutoReadOnly ; Ears
int Property kSlotMask44 = 0x00004000 AutoReadOnly ; Unnamed
int Property kSlotMask45 = 0x00008000 AutoReadOnly ; Unnamed
int Property kSlotMask46 = 0x00010000 AutoReadOnly ; Unnamed
int Property kSlotMask47 = 0x00020000 AutoReadOnly ; Unnamed
int Property kSlotMask48 = 0x00040000 AutoReadOnly ; Unnamed
int Property kSlotMask49 = 0x00080000 AutoReadOnly ; Unnamed
int Property kSlotMask50 = 0x00100000 AutoReadOnly ; DecapitateHead  * masked out
int Property kSlotMask51 = 0x00200000 AutoReadOnly ; Decapitate      * masked out
int Property kSlotMask52 = 0x00400000 AutoReadOnly ; Unnamed
int Property kSlotMask53 = 0x00800000 AutoReadOnly ; Unnamed
int Property kSlotMask54 = 0x01000000 AutoReadOnly ; Unnamed
int Property kSlotMask55 = 0x02000000 AutoReadOnly ; Unnamed
int Property kSlotMask56 = 0x04000000 AutoReadOnly ; Unnamed
int Property kSlotMask57 = 0x08000000 AutoReadOnly ; Unnamed
int Property kSlotMask58 = 0x10000000 AutoReadOnly ; Unnamed
int Property kSlotMask59 = 0x20000000 AutoReadOnly ; Unnamed
int Property kSlotMask60 = 0x40000000 AutoReadOnly ; Unnamed
int Property kSlotMask61 = 0x80000000 AutoReadOnly ; FX01            * masked out
************************************************************ /;

 

 

 

Once more for the future, if you use script code or suggestions provided by other forum users,

its a good idea to say thank you within the source code, not only anonymous inside a forum thread

that will be forgotten next month.

 

I actually put the credit in the mod description where people are actually likely to see it. You can click the link in my signature if you'd like to check. Also, I like to help people, especially those that are trying to help me. I'm sorry that my attempts to help offended you instead.

 

I didn't realize that there were two updates you could simultaneously register for. I'll have to revisit the code and see if I can work that in without breaking anything :wink: Thanks :smile:

 

In the SwapEquipments function, why are you casting the two Actors as ObjectRefrences?

 

I did originally try what you're doing in EquipPlayer with the FormList, and while it did compile, it didn't actually work. Though, looking back on it, I was still making a lot of mistakes with properties, so I'll give it another go and make sure that that's not what was going on.

 

Does threading not spawn a copy of the function and its variables for each thread? If it does, then why have two GetWornEquipment functions? If it doesn't, then that's incredibly bad design.

Link to comment
Share on other sites

How you approach this is going to depend on what exactly you're after. As I understand it, you're trying to do the following:

 

1. Allow the player to swap their equipped gear with that on a mannequin or follower (but not NPCs in the general case?);

2. Allow them to do so either through activating the mannequin or casting a spell on either the mannequin or the follower. (Maybe also through follower dialogue? Not specified yet);

3. Do so in a way that will play relatively nicely with other mannequin-modifying mods;

4. Still allow vanilla activate->show inventory mannequin inventory management to occur.

 

Is that correct? Are there any other pieces to this? Am I missing anything?

 

Well, the minimum functionality was just being able to swap gear with mannequins and followers. Since all NPCs have pre-determined Outfits, it's pretty easy to rule out non-followers, but if people want to mod their games to get rid of Outfits on all NPCs, then this will wind up working on non-followers. For the moment, I've decided not to care about that edge case. If it becomes an issue, I'll add in more explicit logic to identify followers.

 

As for casting a spell on the mannequin, that was my second approach (after giving up on dynamic menus), but I couldn't get the spell to hit the mannequin. Even when I turned off it's isGhost and isInvulnerable flags, it still just ignored the spell. It's why I switched to having it cast the spell at the player on activation instead. Regardless, I wanted to avoid doing it through dialogue since follower dialogue trees can get pretty crowded as it is and a large part of this mod is the convenience factor, which would be reduced by having to go through the dialogue options.

 

It would definitely be nice if it didn't interfere with other mannequin mods, but I wasn't able to manage it. It's not hard to patch my modifications in (assuming you don't care about accessing the inventory directly), but it does currently require a patch.

 

Honestly, I'm a bit on the fence about the fourth one. It wouldn't be that difficult to implement, but it would slow things down compared to my current implementation, and I'm not sure how much use people would actually get out of it.

Link to comment
Share on other sites

Or just rewrite your main function to have it all in a single loop, so that the first equipment swap can start right away. Maybe even add some fancy visual effect that justifies the spell taking it's time. With video games it's more important to provide quick feedback to the action, then to complete it instantly.

Link to comment
Share on other sites

Or just rewrite your main function to have it all in a single loop, so that the first equipment swap can start right away. Maybe even add some fancy visual effect that justifies the spell taking it's time. With video games it's more important to provide quick feedback to the action, then to complete it instantly.

 

It's already giving visual feedback with the healing animation. It just takes a few seconds after that for the actual swap to occur, which is what I was trying to fix.

Link to comment
Share on other sites

@ReDragon2013 I tried both the threading suggestion and the FormList suggestion, neither worked. Not sure if I'm doing something wrong, but as far as I can tell, I'm doing the same thing as you, just with different variable names.

Link to comment
Share on other sites

FWIW RedDragon, I have trouble following your rewrites of my code too. It's a pretty major style clash.

 

@jacob:

 

I *think* I have an approach that can meet most of those things, perhaps even all, but I'll need to do some testing to see if it'll work as I think. My rough plan is to use a perk to add an activation option, similar to the Tower perk, whenever you're trying to activate a default mannequin's linked activator, and a search quest to find all mannequins in a loaded area whenever the player loads a new cell. This will then allow scripts to be applied to those mannequins via alias, which will run only if the custom activation option is chosen. The alias scripts can probably also pre-check the mannequin's inventory for the swap logic.

 

Regarding the followers portion: Is this to work only on the currently active follower(s), or any follower active or not, or any potential follower?

Edited by foamyesque
Link to comment
Share on other sites

FWIW RedDragon, I have trouble following your rewrites of my code too. It's a pretty major style clash.

 

@jacob:

 

I *think* I have an approach that can meet most of those things, perhaps even all, but I'll need to do some testing to see if it'll work as I think. My rough plan is to use a perk to add an activation option, similar to the Tower perk, whenever you're trying to activate a default mannequin's linked activator, and a search quest to find all mannequins in a loaded area whenever the player loads a new cell. This will then allow scripts to be applied to those mannequins via alias, which will run only if the custom activation option is chosen. The alias scripts can probably also pre-check the mannequin's inventory for the swap logic.

 

Regarding the followers portion: Is this to work only on the currently active follower(s), or any follower active or not, or any potential follower?

How would you access the mannequin's inventories? I haven't been able to find anything that would let me do that short of removing everything then sending it all back and monitoring it through an OnAddItem event, which requires a chest for each mannequin. Or do you mean their equipped items?

 

Either way, if the player walks into a room with 50 mannequins, wouldn't polling them all for their outfits create a noticeable lag? Especially for people with lots of mods that use scripts? Or were you thinking something like a cloaking spell with a very limited range so that you'd never catch more than a few mannequins at a time?

 

So, how would the player decide if they wanted normal (whatever that means for them) activation vs. an equipment swap? Tie the perk to whether or not the spell was equipped in the player's hand or something? Would that also be able to switch back to the normal activation if the player unequipped the spell? I've not done much with quests or perks yet so I'm very hazy on the details.

 

At minimum, active followers, at maximum, those that are currently willing to follow. This is mainly meant to make it quick and easy for people to rearrange their and their followers' wardrobes without having to go through a few dozen menus and dialogue trees while the followers constantly make discontent comments about carrying their burdens. I'm sure there are plenty of people out there, especially RPrs and fashionistas, that like to change everyone's outfits frequently. I just wanted to make that process less painful.

Link to comment
Share on other sites

 

FWIW RedDragon, I have trouble following your rewrites of my code too. It's a pretty major style clash.

 

@jacob:

 

I *think* I have an approach that can meet most of those things, perhaps even all, but I'll need to do some testing to see if it'll work as I think. My rough plan is to use a perk to add an activation option, similar to the Tower perk, whenever you're trying to activate a default mannequin's linked activator, and a search quest to find all mannequins in a loaded area whenever the player loads a new cell. This will then allow scripts to be applied to those mannequins via alias, which will run only if the custom activation option is chosen. The alias scripts can probably also pre-check the mannequin's inventory for the swap logic.

 

Regarding the followers portion: Is this to work only on the currently active follower(s), or any follower active or not, or any potential follower?

How would you access the mannequin's inventories? I haven't been able to find anything that would let me do that short of removing everything then sending it all back and monitoring it through an OnAddItem event, which requires a chest for each mannequin. Or do you mean their equipped items?

 

Either way, if the player walks into a room with 50 mannequins, wouldn't polling them all for their outfits create a noticeable lag? Especially for people with lots of mods that use scripts? Or were you thinking something like a cloaking spell with a very limited range so that you'd never catch more than a few mannequins at a time?

 

So, how would the player decide if they wanted normal (whatever that means for them) activation vs. an equipment swap? Tie the perk to whether or not the spell was equipped in the player's hand or something? Would that also be able to switch back to the normal activation if the player unequipped the spell? I've not done much with quests or perks yet so I'm very hazy on the details.

 

At minimum, active followers, at maximum, those that are currently willing to follow. This is mainly meant to make it quick and easy for people to rearrange their and their followers' wardrobes without having to go through a few dozen menus and dialogue trees while the followers constantly make discontent comments about carrying their burdens. I'm sure there are plenty of people out there, especially RPrs and fashionistas, that like to change everyone's outfits frequently. I just wanted to make that process less painful.

 

 

I meant their equipped items, but accessing their inventories is also possible (much less painful with SKSE, but doable even in vanilla). With SKSE, you can use GetContainerForms() to get a Form[] array back, containing every form the actor has at least one of, or alternatively you could iterate through a GetNthForm() loop. GetContainerForms() is by far the better choice; it's not documented on the wiki, but I use it extensively after being pointed to it by cdcooley, and it works beautifully.

 

In vanilla, you'd need to do the dump/onitemremoved combination to build a list, then bring everything on that list back. It's awkward and slow and suboptimal, but possible.

 

 

As regards processing a lot of mannequins: Yes, there's some impingement on the script engine, but because you're running the scripts on separate aliases, you get to take advantage of papyrus's multithreading; they'll all run slower, but they'll also all complete at more or less the same time. The total execution time is generally significantly shorter than running them all in sequence would be, too, because of the way the frame-linked functions work. There's a period of increased vulnerability to stack dumps if something else dramatic happens during the execution, but it isn't terribly likely to happen.

 

W.r.t. the activation option, look at how the Tower perk is implemented in the CK. What happens ingame is that you get a messagebox popup when you activate something (which you can filter through the condition systems) that looks like this:

 

https://youtu.be/lhX108ee0U4?t=320

 

There'll be the normal activation text, that executes whatever OnActivate and default processing occurs, and then there'll be your custom activation option that can be chosen. It's rather obtrusive, but this may let you get things to play nice with other scripts. As I said, I want to test this, since I'm not completely sure of what will happen with competing OnActivate() events. There's some other approaches that could be taken with perks to shift things around too that I might experiment with.

 

W.r.t followers: given the target range, I think a check against PotentialFollowerFaction will be more accurate (and faster) than the GetOutfit() check you're currently using.

Link to comment
Share on other sites

 

I meant their equipped items, but accessing their inventories is also possible (much less painful with SKSE, but doable even in vanilla). With SKSE, you can use GetContainerForms() to get a Form[] array back, containing every form the actor has at least one of, or alternatively you could iterate through a GetNthForm() loop. GetContainerForms() is by far the better choice; it's not documented on the wiki, but I use it extensively after being pointed to it by cdcooley, and it works beautifully.

 

In vanilla, you'd need to do the dump/onitemremoved combination to build a list, then bring everything on that list back. It's awkward and slow and suboptimal, but possible.

 

 

As regards processing a lot of mannequins: Yes, there's some impingement on the script engine, but because you're running the scripts on separate aliases, you get to take advantage of papyrus's multithreading; they'll all run slower, but they'll also all complete at more or less the same time. The total execution time is generally significantly shorter than running them all in sequence would be, too, because of the way the frame-linked functions work. There's a period of increased vulnerability to stack dumps if something else dramatic happens during the execution, but it isn't terribly likely to happen.

 

W.r.t. the activation option, look at how the Tower perk is implemented in the CK. What happens ingame is that you get a messagebox popup when you activate something (which you can filter through the condition systems) that looks like this:

 

https://youtu.be/lhX108ee0U4?t=320

 

There'll be the normal activation text, that executes whatever OnActivate and default processing occurs, and then there'll be your custom activation option that can be chosen. It's rather obtrusive, but this may let you get things to play nice with other scripts. As I said, I want to test this, since I'm not completely sure of what will happen with competing OnActivate() events. There's some other approaches that could be taken with perks to shift things around too that I might experiment with.

 

W.r.t followers: given the target range, I think a check against PotentialFollowerFaction will be more accurate (and faster) than the GetOutfit() check you're currently using.

 

 

You have no idea how much time I've wasted on trying to find a better way to access the entire inventory. It'll help a ton with another mod I'm working on. If I alter one form in the list though, will it alter every instance, or just a random one? Specifically, I'm changing armor and damage ratings on equipment.

 

Ah, I thought you meant the Tower of Strength perk, not the ability you get from the Tower Stone. That sounds like a much better idea than what I've been doing and should completely remove the need for compatibility patches with other mods :D I'll have a look at the perk and see how it's doing what it's doing and if I can replicate it. Thanks :smile:

 

Well, the GetOutfit check is necessary to prevent item duplication. The fact that it also let me find followers was an added bonus that allowed me to not have it as a separate check. Still, if it's an attribute that can be quickly checked, rather than a list that has to be compiled and iterated over, then it's probably worth doing anyway. I'm just not sure if it would cause problems with custom followers. I've not made one before, so I don't know if PotentialFollowerFaction is a required faction for anyone that might follow you.

Link to comment
Share on other sites

  • Recently Browsing   0 members

    • No registered users viewing this page.

×
×
  • Create New...