Jump to content

[LE] Ideas for protecting equipped items from being transferred out of inventory.


corrado33

Recommended Posts

cdcooley wrote: "The game's remove functions actually give preference to removing whatever is equipped first. But the DropObject function does try to remove unequipped items first so there is a workaround if you're willing to do an item-by-item loop and drop items into the world one-by-one and then transfer the resulting reference into some container."

 

It should work like this, hopefully IsharaMeradin forgives me when I touched your provided code.

 

TestItemScript

 

Scriptname TestItemScript extends Quest
; https://forums.nexusmods.com/index.php?/topic/7012536-ideas-for-protecting-equipped-items-from-being-transferred-out-of-inventory/
; IsharaMeradin has given us a function..

  FormList PROPERTY abim_IMS_ModContainers      auto        ; containers
  FormList PROPERTY abim_IMS_MasterItemList     auto        ; items
  FormList PROPERTY abim_IMS_MerchExcludeGVLIst auto        ; merchants excluded items

  ReferenceAlias PROPERTY PlayerAlias auto                  ; should be an quest owned alias ref to the player

  int iReturningItems            ; [default=0]


; -- EVENT --

EVENT OnUpdate()
IF ( iReturningItems )                                ; (iReturningItems == 0)
    RETURN    ; - STOP -    mod has been uninstalled
ENDIF
;---------------------
IF (iReturningItems == 1)
    Debug.Notification("Sorting finished.")
    iReturningItems = 0
    RETURN    ; - STOP -    loop ending announced
ENDIF
;---------------------
    IF (iReturningItems > 2)
        Debug.Notification("Still sorting items. " +(iReturningItems - 2))
    ELSE
        Debug.Notification("Sorting items, please be patient.")
    ENDIF
    
    iReturningItems += 1
    RegisterForSingleUpdate(1.0)        ; update interval 1.0 sec
ENDEVENT


; -- FUNCTIONs -- 2

;----------------------------------------------
FUNCTION ReturnItemsWA(Bool isMerchant = False)
;----------------------------------------------
; sends items back to containers based on lists built as items are added
; "this method is slwower to sort out, but can include weapons and armor in the lists"

    iReturningItems = 2                                    ; <debug>
    RegisterForSingleUpdate(0.0)                           ; <debug>
; ===============================

    actor PlayerRef = PlayerAlias.GetActorReference()      ; Game.GetPlayer()
    int i = abim_IMS_ModContainers.GetSize()                                                                ; i = mcs

    WHILE (i)                                              ; WHILE (i > 0)
        i = i - 1

        IF     (i == 9)                                    ; do not do ammo containers - let those sort out on next bow equip/unequip
        ELSEIF (i == 10)                                   ; do not do ammo containers ..            
        ELSE
;            --------------------------------------------
            IF (!isMerchant) || ((abim_IMS_MerchExcludeGVLIst.GetAt(i) as GlobalVariable).GetValue() == 0)
                formList fml = abim_IMS_MasterItemList.GetAt(i) as FormList                                    ; fml = MasterList
                int n = fml.GetSize()

                objectReference cRef = abim_IMS_ModContainers.GetAt(i) as ObjectReference        ; better, outside the loop !!!
                WHILE (n)                                 ; WHILE (n > 0)
                    n = n - 1

                    form fm = fml.GetAt(n)                                                                    ; fm = Entry
                    int c = PlayerRef.GetItemCount(fm)    ; How many counts of this item to remove?           ; c = q
                    IF ( c )
                        IF (fm as Armor) || (fm as Weapon)

                            IF     PlayerRef.IsEquipped(fm)
                                (PlayerAlias as TestPlayerAliasScript).myF_DropItems(fm, c, cRef)

;;;                            ELSEIF Game.IsObjectFavorited(fm)    ; SKSE Required! Passes if the player has favorited this Item.

                            ELSE
                                PlayerRef.RemoveItem(fm, c, TRUE, cRef)
                            ENDIF

                        ELSE
                                PlayerRef.RemoveItem(fm, c, TRUE, cRef)
                        ENDIF
                    ENDIF
                ENDWHILE
            ENDIF
;            --------------------------------------------
        ENDIF
    ENDWHILE

; ===============================
    iReturningItems = 1                                    ; <debug>
ENDFUNCTION


;--------------------------------------------
FUNCTION ReturnItems(Bool isMerchant = False)
;--------------------------------------------
; sends items back to containers based on lists built as items are added
; "this method is faster to sort out, but cannot include weapons and armor in the lists"

    iReturningItems = 2                                    ; <debug>
    RegisterForSingleUpdate(0.0)                           ; <debug>
; ===============================

    actor PlayerRef = PlayerAlias.GetActorReference()      ; Game.GetPlayer()
    int i = abim_IMS_ModContainers.GetSize()                                                                   ; i = mcs

    WHILE (i)                                              ; WHILE (i > 0)
        i = i - 1

        IF     (i == 9)                                    ; do not do ammo containers - let those sort out on next bow equip/unequip
        ELSEIF (i == 10)                                   ; do not do ammo containers ..            
        ELSE
;            --------------------------------------------
            IF (!isMerchant) || ((abim_IMS_MerchExcludeGVLIst.GetAt(i) as GlobalVariable).GetValue() == 0)
                formList fml = abim_IMS_MasterItemList.GetAt(i) as FormList                                    ; fml = MasterList

                int c = PlayerRef.GetItemCount(fml)        ; How many items (provided by formlist) to remove?
                IF ( c )
                    objectReference cRef = abim_IMS_ModContainers.GetAt(i) as ObjectReference
                    PlayerRef.RemoveItem(fml, c, TRUE, cRef)
                ENDIF
            ENDIF
;            --------------------------------------------
        ENDIF
    ENDWHILE

; ===============================
    iReturningItems = 1                                    ; <debug>
ENDFUNCTION

 

 

 

TestPlayerAliasScript

 

Scriptname TestPlayerAliasScript extends ReferenceAlias
{ReDragon 2018}  ; this should run on the player alias within a running quest

  ObjectReference containerRef

; -- EVENTs --

EVENT OnItemRemoved(Form akBaseItem, Int aiItemCount, ObjectReference akItemReference, ObjectReference akDestContainer)
    ; keeps empty
ENDEVENT


;==============================
state ItemDrop
;=============
EVENT OnItemRemoved(Form akBaseItem, Int aiItemCount, ObjectReference akItemReference, ObjectReference akDestContainer)
;;    Debug.Trace("DropTest: OnItemRemoved() - called for " +akBaseItem+ " with itemRef = " +akItemReference)

IF ( akDestContainer )
    ; no drop here
ELSE
    containerRef.AddItem(akItemReference as Form, 1, TRUE)        ; add every item by hand to the container you like to have
;    https://www.creationkit.com/index.php?title=AddItem_-_ObjectReference
ENDIF
ENDEVENT
;=======
endState


; -- FUNCTION --

;------------------------------------------------------------
 FUNCTION myF_DropItems(Form fm, Int c, ObjectReference cRef)  ; only called by quest script function
;------------------------------------------------------------
; fm   = baseForm of equipped Item
; c    = count of items for the baseForm
; cRef = containerRef as target, see above OnItemRemoved()

IF (c < 2)
    RETURN    ; - STOP -    one item only, the equipped, nothing more to do
ENDIF
;---------------------
    objectReference PlayerRef = self.GetReference()
    containerRef = cRef                                            ; store destination to script variable

; https://www.creationkit.com/index.php?title=DropObject_-_ObjectReference
; "If akObject is equipped, the game will first search for unequipped items in inventory to drop.
; Only when there are no more unequipped items will it drop the equipped items."

    gotoState("ItemDrop")            ; ### STATE ### init state, and handle the item drop by event

    WHILE (c > 1)
        PlayerRef.DropObject(fm, 1)                                ; triggers OnItemRemoved() in this PlayerAlias script
        Utility.Wait(0.1)                   ; just wait, maybe adjustable or removable
        c = c - 1
    ENDWHILE

    gotoState("")                    ; ### STATE ###
    containerRef = None                                            ; release the variable
ENDFUNCTION

 

 

Link to comment
Share on other sites

I was suggesting simply using the return code from the DropObject call to grab the item not adding inventory events to the player.

 

Just a simple while loop in the original code like:

while num_to_drop > 0
    destination_container.AddItem(PlayerRef.DropObject(item_to_drop, 1))
    num_to_drop -= 1
endwhile

When you call AddItem with a reference it moves that specific item into the container rather that creating a new one.

Link to comment
Share on other sites

The reason behind of using the PlayerAliasScript and the event OnItemRemove() is the "ObjectReference broken pointer bug"
researched by taleden. You'll find here: https://www.creationkit.com/index.php?title=ObjectReference_Broken_Pointer_Bug

The vanilla script "WeaponRackActivateSCRIPT" suffers on that bug, because of using something like that:

 objectReference oRef = Game.GetPlayer().DropObject(fm, 1)

This bug is almost happen when the item has an own script attached, for example silversword.

 

Out of topic, an integer conversion function to make a hexadecimal output string.

 

 

 

;-----------------------------------------------
String FUNCTION Dec2Hex(Int i10, String sAdd="") Global
;-----------------------------------------------
; convert integer (i10) into hexstring with count of 8
; 842 (base 10) = 34A (base 16) -> 3*16² + 4*16 + A*1;    842 -> 0x0000034A

string s
int v = 0x10000000               ; init divisor
;    -------------
    WHILE (v > 0)
        int j = i10 / v

        IF (j < 10)
            s += j as String
        ELSEIF (j == 10)
            s += "A"
        ELSEIF (j == 11)
            s += "B"
        ELSEIF (j == 12)
            s += "C"
        ELSEIF (j == 13)
            s += "D"
        ELSEIF (j == 14)
            s += "E"
        ELSE ;IF (j == 15)
            s += "F"
;;        ELSE
;;            s += "?"
        ENDIF

        i10 = i10 % v            ; new remainder

        IF (v > 1)
            v = v / 16           ; new divisor
        ELSE
            v = 0                ; 1 / 16 = ?    safety first
        ENDIF
    ENDWHILE
;    -------------
    IF     (sAdd == "0x") || (sAdd == "$")
        s = sAdd + s
    ELSEIF (sAdd == "")          ; no substring
    ELSE
        s = s + sAdd
    ENDIF

    RETURN s
ENDFUNCTION

 

 

Link to comment
Share on other sites

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...