ReDragon2013 Posted September 25, 2018 Share Posted September 25, 2018 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 More sharing options...
cdcooley Posted September 26, 2018 Share Posted September 26, 2018 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 More sharing options...
ReDragon2013 Posted September 27, 2018 Share Posted September 27, 2018 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_BugThe 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 More sharing options...
Recommended Posts