Jump to content

Removing the notifications from RemoveAllItems used on player


DiodeLadder

Recommended Posts

In empathy one of my fast start quest bypass mods was generating 8 minutes (that's 2 hours of game-time) of on screen quest start notifications due to setting quest bypass stages in its 10 second run time.

 

With perseverance I got that down to one message @ 30 seconds (damn you, foul MQ104 Stage 50). An imperfect compromise, but good enough for government work.

Link to comment
Share on other sites

  • Replies 40
  • Created
  • Last Reply

Top Posters In This Topic

I just had this idea (edit: added in -1 count as per Wolfmark's reminder below):

Function RemoveAllItemsSilent(ObjectReference akObjRef, ObjectReference DestContainerRef)
	akObjRef.RemoveItem(KeywordFormList, -1, true, DestContainerRef)
EndFunction
And if the above doesn't work, the longer version:

Function RemoveAllItemsSilent(ObjectReference akObjRef, ObjectReference DestContainerRef)
	int i = 0
	while (i < KeywordFormList.GetSize())
		Keyword currentkey = KeywordFormList.GetAt(i) As Keyword
		akObjRef.RemoveItem(currentkey, -1, true, DestContainerRef)
		i += 1
	endWhile
EndFunction
Edited by Reneer
Link to comment
Share on other sites

At this point, the only posible way is by using script extender's GetInventoryItems():

Form[] Property BlackList Auto Const

Function RemoveAllItemsSilent(ObjectReference akActorRef, ObjectReference DestContainerRef)
	Form[] InventoryItems = akActorRef.GetInventoryItems()
	Int i = 0
	While i < InventoryItems.Length
		Form CurrentItem = InventoryItems[i]
		If(BlackList.Find(CurrentItem) < 0)
			Int ItemCount = akActorRef.GetItemCount(akItem = CurrentItem)
			akActorRef.RemoveItem(akItemToRemove = CurrentItem, aiCount = ItemCount, abSilent = True, akOtherContainer = DestContainerRef)
		EndIf
		i += 1
	EndWhile
EndFunction

I've added property which stores a blacklist of the objects that you don't want to be removed from the actor, so you can add the Pipboy, or whatever object, to this array, and it will be ignored. Unfortunately its still too slow:

 

PS: At the moment I've started to test it, the answer from Reneer wasn't there. ^^ You may want to try it.

Edited by DieFeM
Link to comment
Share on other sites

I had that exact same latency with my auto inventory junk scrapper mod until I split the search/find/evaluation list into 6 shards with 100 MISC items in each list and ran seperate threads on them. You may be able to refactor this code:

; ****** Multithread the item list evaluation and move to interim container

Var[] vParams = new Var[3]
vParams[0] = SourceREF as Objectreference
vParams[1] = TempContainerREF as Objectreference
vParams[2] = akItemListToUse as FormList 

vParams[2] = pSKK_ScrapperSafeItemList01 as FormList 
pSKK_ScrapperThread01.CallFunctionNoWait("MoveItems", vParams)
vParams[2] = pSKK_ScrapperSafeItemList02 as FormList 
pSKK_ScrapperThread02.CallFunctionNoWait("MoveItems", vParams)
vParams[2] = pSKK_ScrapperSafeItemList03 as FormList 
pSKK_ScrapperThread03.CallFunctionNoWait("MoveItems", vParams)
vParams[2] = pSKK_ScrapperSafeItemList04 as FormList 
pSKK_ScrapperThread04.CallFunctionNoWait("MoveItems", vParams)
vParams[2] = pSKK_ScrapperSafeItemList05 as FormList 
pSKK_ScrapperThread05.CallFunctionNoWait("MoveItems", vParams)
vParams[2] = pSKK_ScrapperSafeItemList06 as FormList 
pSKK_ScrapperThread06.CallFunctionNoWait("MoveItems", vParams)
vParams[2] = pSKK_ScrapperDLC03ItemList as FormList 
pSKK_ScrapperThread07.CallFunctionNoWait("MoveItems", vParams)
vParams[2] = pSKK_ScrapperDLC04ItemList as FormList 
pSKK_ScrapperThread08.CallFunctionNoWait("MoveItems", vParams)
vParams[2] = pSKK_ScrapperModItemList as FormList
pSKK_ScrapperThread09.CallFunctionNoWait("MoveItems", vParams)

Utility.WaitMenuMode(1) ; wait for threadlocks to engage

While pSKK_ScrapperThread01.ThreadLocked() && pSKK_ScrapperThread02.ThreadLocked() && pSKK_ScrapperThread03.ThreadLocked() && pSKK_ScrapperThread04.ThreadLocked() && pSKK_ScrapperThread05.ThreadLocked() && pSKK_ScrapperThread06.ThreadLocked() && pSKK_ScrapperThread07.ThreadLocked() && pSKK_ScrapperThread08.ThreadLocked() && pSKK_ScrapperThread09.ThreadLocked()
	Utility.WaitMenuMode(1)  ; wait for threadlocks to complete
EndWhile
Link to comment
Share on other sites

The aiCount parameter for RemoveItem can be -1, which means "all items".

There's no need to call GetItemCount() to obtain the number and then call RemoveItem() with that number.

 

A faster solution:

 

1. Create a form list with all ObjectTypeXXX keywords and all scrap components (c_XXX records). Add few other common keywords, like UnscrappableObject, BobbleheadKeyword, FeaturedItem etc...

2. Call RemoveItem() with this form list and -1 for aiCount parameter.

3. I would create a new form list with all other vanilla items that don't have a keyword or components. Like all holotapes, all keys, all books etc...

4. Call RemoveItem() with this second form list and -1 for aiCount parameter.

 

(you can merge the first and second form lists)

 

This should remove most of junk items + weapons + ammo + armor + chems + holotapes + keys + notes. With only 1-2 RemoveItem() calls. There are few exceptions, for example some ammo records don't have ObjectTypeAmmo keyword (flares, gamma rounds...).

 

Then if F4SE is installed:

 

5. Create an empty form list property.

6. Use ObjectReference.GetInventoryItems() to get the base forms for all remaining items and move them to the form list. This is the slow part and this is why is better to remove as much as possible in first 1-4 steps...

7. Call RemoveItem with the new form list and -1 for aiCount parameter.

 

Without F4SE installed the idea is to minimize the number of notification messages:

 

5. I would create a form list with most vanilla items that were not included in steps 1-4.

6. RemoveItem with this form list and -1 for aiCount parameter.

7. RemoveAllItems() for remaining items.

Link to comment
Share on other sites

The aiCount parameter for RemoveItem can be -1, which means "all items".

There's no need to call GetItemCount() to obtain the number and then call RemoveItem() with that number.

I always forget about that little trick. Either way, nice explanation of how the code I wrote works.
Link to comment
Share on other sites

 

I just had this idea:

Function RemoveAllItemsSilent(ObjectReference akObjRef, ObjectReference DestContainerRef)
	akObjRef.RemoveItem(KeywordFormList, akObjRef.GetItemCount(KeywordFormList), true, DestContainerRef)
EndFunction

And if the above doesn't work, the longer version:

Function RemoveAllItemsSilent(ObjectReference akObjRef, ObjectReference DestContainerRef)
	int i = 0
	while (i < KeywordFormList.GetSize())
		Keyword currentkey = KeywordFormList.GetAt(i) As Keyword
		int count = akObjRef.GetItemCount(currentkey)
		akObjRef.RemoveItem(currentkey, count, true, DestContainerRef)
		i += 1
	endWhile
EndFunction

 

Thank you very much Reneer! The first one was actually something I have tried myself earlier, but catching everything was the part I couldn't figure out at the time, because not everything in player inventory has attached keywords. Would you happen to know a way to catch everything? If I can catch everything with this, I think I can make it work. I think I threw in all the keywords I could think of, but misc items (and maybe few other things?) were the ones I couldn't catch. It does work with a formlist without doing the while loop, BTW, the first one worked for me.

 

By the way, for the RemoveItem, I could use a negative integer value as the item count to remove all the items with keywords on the formlist in one shot if I remember correctly, and I think there was no need to make calls to get the actual item counts.

 

 

At this point, the only posible way is by using script extender's GetInventoryItems():

Form[] Property BlackList Auto Const

Function RemoveAllItemsSilent(ObjectReference akActorRef, ObjectReference DestContainerRef)
	Form[] InventoryItems = akActorRef.GetInventoryItems()
	Int i = 0
	While i < InventoryItems.Length
		Form CurrentItem = InventoryItems[i]
		If(BlackList.Find(CurrentItem) < 0)
			Int ItemCount = akActorRef.GetItemCount(akItem = CurrentItem)
			akActorRef.RemoveItem(akItemToRemove = CurrentItem, aiCount = ItemCount, abSilent = True, akOtherContainer = DestContainerRef)
		EndIf
		i += 1
	EndWhile
EndFunction

I've added property which stores a blacklist of the objects that you don't want to be removed from the actor, so you can add the Pipboy, or whatever object, to this array, and it will be ignored. Unfortunately its still too slow:

 

PS: At the moment I've started to test it, the answer from Reneer wasn't there. ^^ You may want to try it.

 

Thank you very much DieFeM, for even taking time to make a video! Using SE is something I would want to avoid in this mod, though.... That sure would be handy to have, however, and tempting.

 

If I remember correctly, RemoveItem by itself would not remove the equipped items, and I think what I learned from another forum post was to use a loop to unequip items on armor slot 0-29 and avoid 30 which is Pip-Boy (I've read that the actual slot numbers used by Papyrus are different from what you see in CK, which are 30 through 60). UnequipAll was a no-go for me because it removed Pip-boy.

 

Quest items getting removed is okay I think, because they would be in the destination container anyway, so it maybe possible to get away without defining the blacklist items. ...Maybe that would give a good opportunity to ditch the stuff from Cambridge Polymer, lol.

 

 

 

I had that exact same latency with my auto inventory junk scrapper mod until I split the search/find/evaluation list into 6 shards with 100 MISC items in each list and ran seperate threads on them. You may be able to refactor this code:

; ****** Multithread the item list evaluation and move to interim container

Var[] vParams = new Var[3]
vParams[0] = SourceREF as Objectreference
vParams[1] = TempContainerREF as Objectreference
vParams[2] = akItemListToUse as FormList 

vParams[2] = pSKK_ScrapperSafeItemList01 as FormList 
pSKK_ScrapperThread01.CallFunctionNoWait("MoveItems", vParams)
vParams[2] = pSKK_ScrapperSafeItemList02 as FormList 
pSKK_ScrapperThread02.CallFunctionNoWait("MoveItems", vParams)
vParams[2] = pSKK_ScrapperSafeItemList03 as FormList 
pSKK_ScrapperThread03.CallFunctionNoWait("MoveItems", vParams)
vParams[2] = pSKK_ScrapperSafeItemList04 as FormList 
pSKK_ScrapperThread04.CallFunctionNoWait("MoveItems", vParams)
vParams[2] = pSKK_ScrapperSafeItemList05 as FormList 
pSKK_ScrapperThread05.CallFunctionNoWait("MoveItems", vParams)
vParams[2] = pSKK_ScrapperSafeItemList06 as FormList 
pSKK_ScrapperThread06.CallFunctionNoWait("MoveItems", vParams)
vParams[2] = pSKK_ScrapperDLC03ItemList as FormList 
pSKK_ScrapperThread07.CallFunctionNoWait("MoveItems", vParams)
vParams[2] = pSKK_ScrapperDLC04ItemList as FormList 
pSKK_ScrapperThread08.CallFunctionNoWait("MoveItems", vParams)
vParams[2] = pSKK_ScrapperModItemList as FormList
pSKK_ScrapperThread09.CallFunctionNoWait("MoveItems", vParams)

Utility.WaitMenuMode(1) ; wait for threadlocks to engage

While pSKK_ScrapperThread01.ThreadLocked() && pSKK_ScrapperThread02.ThreadLocked() && pSKK_ScrapperThread03.ThreadLocked() && pSKK_ScrapperThread04.ThreadLocked() && pSKK_ScrapperThread05.ThreadLocked() && pSKK_ScrapperThread06.ThreadLocked() && pSKK_ScrapperThread07.ThreadLocked() && pSKK_ScrapperThread08.ThreadLocked() && pSKK_ScrapperThread09.ThreadLocked()
	Utility.WaitMenuMode(1)  ; wait for threadlocks to complete
EndWhile

 

Thank you very much SKK, but this is totally over my head, lol. So you've broken up the list of items to shorten the scanning time...? Is it necessary to create a formlist with all individual misc items to catch them? That was the part I couldn't figure out, because they don't have any keywords attached.

 

I guess the problem with relying on keywords and pre-made formlist is that, if the player has non-vanilla items without keywords, those would not be removed. The SE function DieFeM mentioned would be able to really catch everything on the other hand...

Edited by DiodeLadder
Link to comment
Share on other sites

The aiCount parameter for RemoveItem can be -1, which means "all items".

There's no need to call GetItemCount() to obtain the number and then call RemoveItem() with that number.

 

A faster solution:

 

1. Create a form list with all ObjectTypeXXX keywords and all scrap components (c_XXX records). Add few other common keywords, like UnscrappableObject, BobbleheadKeyword, FeaturedItem etc...

2. Call RemoveItem() with this form list and -1 for aiCount parameter.

3. I would create a new form list with all other vanilla items that don't have a keyword or components. Like all holotapes, all keys, all books etc...

4. Call RemoveItem() with this second form list and -1 for aiCount parameter.

 

(you can merge the first and second form lists)

 

This should remove most of junk items + weapons + ammo + armor + chems + holotapes + keys + notes. With only 1-2 RemoveItem() calls. There are few exceptions, for example some ammo records don't have ObjectTypeAmmo keyword (flares, gamma rounds...).

 

Then if F4SE is installed:

 

5. Create an empty form list property.

6. Use ObjectReference.GetInventoryItems() to get the base forms for all remaining items and move them to the form list. This is the slow part and this is why is better to remove as much as possible in first 1-4 steps...

7. Call RemoveItem with the new form list and -1 for aiCount parameter.

 

Without F4SE installed the idea is to minimize the number of notification messages:

 

5. I would create a form list with most vanilla items that were not included in steps 1-4.

6. RemoveItem with this form list and -1 for aiCount parameter.

7. RemoveAllItems() for remaining items.

 

Thank you very much for chiming in Wolfmark!

 

Yeah, how to deal with those items without keywords was what I couldn't figure out. So I really do need to make a formlist with them then... Thank you for clarifying!

Link to comment
Share on other sites

After reading Wolfmark's reply, still using F4SE, I've implemented some of the things he said:

FormList Property ScriptFilledFormList Auto Const
Form Property Pipboy Auto Const

Bool Function FillFormList(Form[] Items)
	ScriptFilledFormList.Revert()
	Int i = 0
	While i < Items.Length
		Form CurrentForm = Items[i]
		If(Pipboy != CurrentForm)
			ScriptFilledFormList.AddForm(CurrentForm)
		EndIf
		i += 1
	EndWhile
	Return True
EndFunction

Function RemoveAllItemsSilent(ObjectReference akActorRef, ObjectReference DestContainerRef)
	If(FillFormList(akActorRef.GetInventoryItems()))
		akActorRef.RemoveItem(akItemToRemove = ScriptFilledFormList, aiCount = -1, abSilent = True, akOtherContainer = DestContainerRef)
	EndIf
EndFunction

It changes the behavior of the function as is expected: takes a second or 2 to fill the FormList, but then it removes everything in a fraction of second.

Edited by DieFeM
Link to comment
Share on other sites

  • Recently Browsing   0 members

    • No registered users viewing this page.

×
×
  • Create New...