Script: remove certain item types from inventory

Post » Tue Jun 19, 2012 1:31 am

Hi there,

I'm trying to re-create some of my Oblivion/Fallout mods.
Those need to loop over all items in the player's inventory and remove items of a certain type, in particular
  • arrows
  • potions
  • crafting materials (ore, ingots, leather, ...)
  • alchemy ingredients
  • soul gems
Naturally I don't want to check every item's ID but would prefer to use a boolean function (IsIngredient, IsAmmo) or at least check members of a predefined list.

In the CK I only found
  • lists: ArrowTypeFormList, CookingIngredientsList, SoulGemsAll
  • form types: AMMO, INGR, SLGM.
Apart from the fact that I don't know yet how to code the checks, I didn't find a CK attribute I can use for all of the item types I'd like to handle (there's no AlchemyIngredientsList, no SmithingIngredientsList and smithing material is stored as form type MISC).

Any idea? Or should I wait for appropriate SKSE functions?

I find the CK-Wiki hard to navigate, is there a page that lists, for example, all valid arguments to AddInventoryEventFilter? (CookingIngredientsList is the one in the example)

Many thanks in advance!
User avatar
Victoria Bartel
 
Posts: 3325
Joined: Tue Apr 10, 2007 10:20 am

Post » Tue Jun 19, 2012 3:50 pm

I've looked into getting something similar done but I haven't been able to find something neat like the http://geck.gamesas.com/index.php/RemoveAllTypedItemsof new vegas.
I can only think of a quick way to remove all vanilla items-> by putting those in form list and comparing with the container by using http://www.creationkit.com/AddInventoryEventFilter_-_ObjectReference. (you need a new form list as it won't take nested ones)
But that won't work for modded items, http://www.creationkit.com/GetIsFormType might be useful to be added to SKSE.
There might another way, but I haven't spotted it as I'm not that well into papyrus yet :confused:.
User avatar
butterfly
 
Posts: 3467
Joined: Wed Aug 16, 2006 8:20 pm

Post » Tue Jun 19, 2012 3:06 am

I asked the very same question days ago and got zero helpful replies. I wanted to set up a button that when activated would remove all item types (say Staves) and put them in a container. Basically, an automatic inventory sorter. But unfortunately no one has responded with any help. If you do figure out anything, please post it back here. I’ll follow this thread as well. (Man, RemoveAllTypedItems would have been so easy. I think this is going to be confusing and complex for such a simple function.)
User avatar
Gisela Amaya
 
Posts: 3424
Joined: Tue Oct 23, 2007 4:29 pm

Post » Tue Jun 19, 2012 7:43 am

Without the ability to loop through inventory items, there isn't really any way to look into a container's inventory unless you already know the specific items that you're looking for.

You could try coupling a couple of calls to http://www.creationkit.com/RemoveAllItems_-_ObjectReference with an http://www.creationkit.com/OnItemAdded_-_ObjectReference event to detect the items currently in a container and, perhaps, build a FormList with this information.

Cipscis
User avatar
Kate Murrell
 
Posts: 3537
Joined: Mon Oct 16, 2006 4:02 am

Post » Tue Jun 19, 2012 8:52 am

http://www.creationkit.com/GetIsFormType might be useful to be added to SKSE.
There might another way, but I haven't spotted it as I'm not that well into papyrus yet :confused:.

There is a way to check the types. I noticed it while looking at the ingame Bookshelf code.
Cast the object to the type you are checking. If it casts to None then it's not the right type of object.

From the in-game script PlayerBookShelfContainerScript
Event OnItemAdded(Form akBaseItem, int aiItemCount, ObjectReference akItemReference, ObjectReference akSourceContainer);Trace("BOOKCASE - Adding " + akBaseItem + " to the Book Container")if (akBaseItem as Book) ; <------ This is our new GetIsFormType syntax :); If the item being added is a book then check to see if there is room in on the shelf.;Trace("BOOKCASE - Form being Added " + akBaseItem + " is a Book!")
User avatar
Kristina Campbell
 
Posts: 3512
Joined: Sun Oct 15, 2006 7:08 am

Post » Tue Jun 19, 2012 11:10 am

Hello all, thank you very much for your answers!

Usually I hate asking questions that are answered by RTFM but just now it seems to me I don't know enough to even locate the info in the docs. So I apologize in advance!

I can only think of a quick way to remove all vanilla items-> by putting those in form list and comparing with the container by using http://www.creationkit.com/AddInventoryEventFilter_-_ObjectReference. (you need a new form list as it won't take nested ones) But that won't work for modded items
This seems to be the approach to items not covered by their own form (crafting materials being MISC). I guess one could write a couple of lists and use only one script handling a particular list as property, thus having neatly separated code from data ;-)

The con, as you said, is that the lists will have to be adjusted for other mods. And I really don't want to put all of the alchemy ingredients into a list, manually (CK has File-Export-Ammo, but nothing for the other item types I'm interested in).

Does someone know
  • of a way to extract all items of a given form type from the CK
  • where the ingredients for a recipe are stored (COBJ forms? ) and can be extracted (I mean raw materials for cooked/crafted items)
  • if there is a way (dreaming...) of defining a list externally by putting the strings into an config file and build a list suitable for use with AddInventoryEventFilter
While writing, I realize I cannot add items to a duplicated FLST? I thought I read that someone had done this....
(Edit: found it: you have to drag a new item into the list, did I mention I hate GUIs for repetitive actions?)
  • I hope, AddForm works on a user-generated empty list?
  • Where would I put the population of the list? I guess to AddForm the same thing multiple times would be bad? What's the lifetime of a modified FLST - just for script execution, for a game session or even stored into the savegame? Depending on the answer the list generation would have to be done every time the script is called, once at game start or at game start including a check if the item added is already present.
I wanted to set up a button that when activated would remove all item types (say Staves) and put them in a container.
That's the same thing I'd like to do: some containers that will fetch items of a certain kind from the inventory and, expanding that, a port of my old http://tes.nexusmods.com/downloads/file.php?id=23857 mod.

Without the ability to loop through inventory items, there isn't really any way to look into a container's inventory unless you already know the specific items that you're looking for. You could try coupling a couple of calls to http://www.creationkit.com/RemoveAllItems_-_ObjectReference with an http://www.creationkit.com/OnItemAdded_-_ObjectReference event to detect the items currently in a container and, perhaps, build a FormList with this information. Cipscis
So for inventory looping I'll have to wait for SKSE? And something like (OBSE)
let Items := player.GetItems ItemID
is out of the question for now?

Regarding RemoveAllItems: at least using this to put all items from the player into another container, filter them using OnItemAdded and put those not to store back into the player's inventory seems quite dangerous because items equipped will also be affected and it would be quite complicated to handle putting them back on.
Perhaps a combination of AddInventoryEnventFilter, OnItemRemoved (from player) and RemoveAllItems will be possible?

There is a way to check the types. I noticed it while looking at the ingame Bookshelf code. Cast the object to the type you are checking. If it casts to None then it's not the right type of object.
That's interesting. Though it will only work when there is a suitable type to check against. So definitely not for ores/ingots as they are MISC items. But arrows could be handled using AMMO and there is a definite appeal to using INGR instaed of building an alchemy ingredient list manually...

Returning to the apologizing part: I'd be grateful to everyone who could share some code snippets regarding the usage of the Papyrus methods mentioned above. That alone would greatly speed my progress (yesterday I needed 2 hours just to attach a debug.notification script to a container activation...)
Spoiler
Scriptname EK_ArrowBarrelScript extends ObjectReferenceEvent OnActivate(ObjectReference akActionRef)	if (akActionRef == Game.GetPlayer())	  	   Debug.Notification("EK_ArrowBarrelScript saw player")	   Game.GetPlayer().RemoveAllItems(akTransferTo = None, \				  abKeepOwnership = true, \				  abRemoveQuestItems = false)	  	endifEndEvent

And, on a last note, do you have a pointer to something telling me how to implement a simple yes/no choicebox, similar to the one I used in this Fallout Code?
Spoiler
Begin OnActivate  if ( IsActionRef Player == 1 )	ShowMessage EKAmmoLockerMsg1	set sStage to 1  else	set sStage to 1	Activate  endifendBegin GameMode  if sStage == 0	return  endif  set Button to GetButtonPressed  if ( Button == 0 )	; do something special  endif  set sStage to 0  Activateend

Edit: I'm struggeling a bit with the basic concepts. Say I want to add an item defined in the CK to a list (or just do something with it in the Papyrus script (I tried Game.GetPlayer().AddItem)), is it correct that
  • in the script I have to say something like
    MiscObject property GemDiamond auto
    because
    • MiscObject is the Papyrus-equivalent for CK's MISC
    • GemDiamond is defined in the CK and using it as property name enables the 'AutoFill' button to generate the connections correctly
  • after script compilation I have to use AutoFill for all script properties to enable correct references from the script to the game world
Extending this I could create a FLST in the CK (dragging...) and declare it as a property in the script?
User avatar
Harry Leon
 
Posts: 3381
Joined: Tue Jun 12, 2007 3:53 am

Post » Tue Jun 19, 2012 5:35 am

If you want, ive released my source code for CUPID, which could be useful to you, demonstrating the removal of items. I only have them effect Ammo
It has support for Custom Mods that have there own custom arrows etc, by manipulating FormList
  • FormList Property AmmoList Auto			 ;This is where we hold custom ammo, or what the player has ever equipped during this session)FormList Property AmmoDefList Auto		 ;Holds the default ammo found in Skyrim, that is useable via player 
Spoiler

ScriptName CUPID_OnPlayerEquip Extends ReferenceAlias  ; Cupid 0.4a; Revised script, no longer attached to Player;; Version; 0.4a; - Added the ability to enable / disable cupid via Global Variable; - Fixed Bound Bow problems when the player had more arrows than the bound arrow scripted EventFormList Property AmmoList AutoFormList Property AmmoDefList AutoInt Property MaxAmmoInList AutoKeyword Property WeapTypeBow AutoKeyword Property VendorItemArrow AutoKeyword Property WeapTypeBoundArrow AutoFloat Property CupidUpdateIntervalve = 1.0 AutoGlobalVariable Property CupidInstalled AutoBool bUpdating = FalseForm formAmmoBool bKeepAlive = TrueBool Function IsCupidInstalled()	If CupidInstalled.GetValueInt() == 0		Return False	EndIf	Return TrueEndFunctionFunction DebugMessage(String msg)	;Debug.Notification(msg)EndFunction;Detects if Cupid is installed if not we will no longer call our items;Why are we updating? well incase we happen to equip arrows and when;the bow is no longer presentFunction CallUpdate()	If !IsCupidInstalled()		bKeepAlive = False		Return	EndIf		If !bUpdating		bUpdating = True		RegisterForSingleUpdate(CupidUpdateIntervalve)	EndIfEndFunctionEvent OnUpdate()	DebugMessage("OnUpdate")	Actor player = GetActorRef()	Bool IsArrowVendorEquipped = (player.WornHasKeyword(VendorItemArrow))	Bool IsBoundBow = (player.WornHasKeyword(WeapTypeBoundArrow))	Bool IsBowEquipped = (player.GetEquippedItemType(1) == 7 || player.GetEquippedItemType(0) == 7)		If !IsBowEquipped && IsArrowVendorEquipped		UnequipArrows(player)	EndIf		If bKeepAlive		RegisterForSingleUpdate(CupidUpdateIntervalve)	EndIf	EndEventForm Function GrabAmmoFromList()	Int i = 0	Int save_count = 0	Int item_count = 0	Int equal_count = 0	Form save_form = None	Actor player = GetActorRef()		While i < AmmoList.GetSize()		item_count = player.GetItemCount(AmmoList.GetAt(i))		If item_count > save_count			save_count = item_count			save_form = AmmoList.GetAt(i)		EndIf		i += 1	EndWhile		;Incase we lagged while doing the loop, lets recheck	Bool IsBoundBow = (player.WornHasKeyword(WeapTypeBoundArrow))	Bool IsBowEquipped = (player.GetEquippedItemType(1) == 7 || player.GetEquippedItemType(0) == 7)	If !IsBowEquipped		save_form = None	EndIf		Return save_formEndFunctionFunction EquipArrows(Actor akPlayer)		Bool IsArrowVendorEquipped = (akPlayer.WornHasKeyword(VendorItemArrow))	Bool IsBoundBow = (akPlayer.WornHasKeyword(WeapTypeBoundArrow))	Bool IsBowEquipped = (akPlayer.GetEquippedItemType(1) == 7 || akPlayer.GetEquippedItemType(0) == 7)		If IsBoundBow		Return	EndIf		If IsBowEquipped && !IsBoundBow		If AmmoList != None			Form new_form = GrabAmmoFromList()			If new_form != None				If !akPlayer.IsEquipped(new_form)					akPlayer.EquipItem(new_form,False,True)				EndIf			Else			EndIf		EndIf	ElseIf !IsBowEquipped && !IsBoundBow && IsArrowVendorEquipped		UnequipArrows(akPlayer)	EndIfEndFunctionFunction UnequipArrows(Actor akPlayer)	Int i = 0	Int count = AmmoList.GetSize()	While i < count		If akPlayer.IsEquipped(AmmoList.GetAt(i))			akPlayer.UnequipItem(AmmoList.GetAt(i))			i = count		EndIf		i += 1	EndWhileEndFunctionEvent OnItemAdded(Form akBaseItem, Int aiItemCount, ObjectReference akItemReference, ObjectReference akSourceContainer)	DebugMessage("OnItemAdded")	If IsCupidInstalled()		If akBaseItem As Ammo			If !AmmoList.HasForm(akBaseItem)				;Debug.Notification(akBaseItem + " Added in List")				AmmoList.AddForm(akBaseItem)			EndIf		EndIf	EndIfEndEventEvent OnItemRemoved(Form akBaseItem, Int aiItemCount, ObjectReference akItemReference, ObjectReference akDestContainer)	DebugMessage("OnItemRemoved")	If IsCupidInstalled()		If akBaseItem As Ammo			;Exit out so we dont accidently remove the default ammo			If AmmoDefList.HasForm(akBaseItem)				;Debug.Notification("We are returning")			Else				;Check to see if we are adding a new Ammo to the list				If AmmoList.HasForm(akBaseItem)					;Debug.Notification("Removed Item from List " + akBaseItem)					AmmoList.RemoveAddedForm(akBaseItem)				EndIf			EndIf		EndIf	EndIfEndEventEvent OnObjectEquipped(Form akBaseObject, ObjectReference akReference)	DebugMessage("OnObjectEquipped")	If IsCupidInstalled()		Actor player = GetActorRef()				CallUpdate()		If akBaseObject As Weapon						EquipArrows(player)		EndIf				If akBaseObject As Ammo			formAmmo = akBaseObject As Form				If !AmmoList.HasForm(formAmmo)				AmmoList.AddForm(formAmmo)			EndIf		EndIf	EndIfEndEventEvent OnObjectUnequipped(Form akBaseObject, ObjectReference akReference)	DebugMessage("OnObjectUnequipped")	If IsCupidInstalled()		Actor player = GetActorRef()		Bool IsArrowVendorEquipped = (player.WornHasKeyword(VendorItemArrow))		Bool IsBoundBow = (player.WornHasKeyword(WeapTypeBoundArrow))				If IsBoundBow			Return		EndIf				CallUpdate()		If akBaseObject As Weapon			If player.GetEquippedItemType(0) != 7 && !IsBoundBow				player.UnequipItem(formAmmo,False,True)			EndIf		EndIf				If akBaseObject As Ammo			formAmmo = akBaseObject As Form			If !AmmoList.HasForm(formAmmo)				AmmoList.AddForm(formAmmo)			EndIf						If player.GetEquippedItemType(0) == 7 && !IsBoundBow && !IsArrowVendorEquipped				EquipArrows(player)			EndIf		EndIf	EndIfEndEvent
User avatar
Dalton Greynolds
 
Posts: 3476
Joined: Thu Oct 18, 2007 5:12 pm

Post » Tue Jun 19, 2012 8:31 am

do items have keywords? I havent even dabbled in anything otuside of spells right now, but perhaps you can do searches for keywords atatched to items, and do stuff to them?
User avatar
daniel royle
 
Posts: 3439
Joined: Thu May 17, 2007 8:44 am

Post » Tue Jun 19, 2012 8:40 am

Hello again,

@taewon: thanks for the sneak peek, it helped my progress very much.

I'm now rather happy with the simple result:
  • one script EK_TransferItemsByList is handling all lists
  • each list (bspw EK_VanillaArrows) is constructed in the CK (copy an existing list, remove all entries, fill by dragging)
  • the script is attached to a container and one of the lists is connected to the ItemsToTransfer property
Spoiler
Scriptname EK_TransferItemsByList extends ObjectReference; bind to externally defined FLST of items to handleFormList Property ItemsToTransfer AutoObjectReference ObjectActivatingContainerObjectReference ThisContainerForm ThisItemint iItemint nItemsint nThisItemEvent OnActivate(ObjectReference akActionRef)   ObjectActivatingContainer = akActionRef   ThisContainer			 = self	if ( ObjectActivatingContainer == Game.GetPlayer() )	  	  nItems = ItemsToTransfer.GetSize()	  iItem  = 0	  while ( iItem < nItems )		 ThisItem  = ItemsToTransfer.GetAt(iItem)		 nThisItem = Game.GetPlayer().GetItemCount(ThisItem)		 if ( nThisItem > 0 )			Game.GetPlayer().RemoveItem(ThisItem, nThisItem, true, ThisContainer)		 endif		 iItem = iItem + 1	  endwhile	endif  EndEvent

On the negative side the result is only valid for vanilla items, but I think I'll wait for SKSE giving us ItemIDs. The bright side is that due to the lack of an automagism to fill the lists I can e.g. add DaedraHearts to the list for smithing instead of alchemy ingredients.

Now there are two things I'd still like to know:
  • how to add the interactive choice box (put away your arrows (yes/no))
  • how to ensure that the open container menu shows the transferred items immediately, so I don't have to re-open it (-> some wait statement or flush-command?)
Naturally I still dream of this user-configurable list of items.. ;-)

So, thanks again. The result is very simple and naturally makes me think I've been stupid. Yet - do you think the Wiki might benefit from some more complete examples like this?


Final edit: my fears about filling the alchemy ingredients list were completely unnecessary: CK, display all, sort by form id, the ingredients can be added all at once just using the usual shift-click for marking.
User avatar
MISS KEEP UR
 
Posts: 3384
Joined: Sat Aug 26, 2006 6:26 am


Return to V - Skyrim