Jump to content

[LE] Container script - Detect if specific items are taken / container is empty?


Uledus

Recommended Posts

For some time now I can't figure this out and I hope someone can help me with my problem.

I want a message box as soon as the player got everything from a container with Ref ID "Chest".

- The container has the following items - The message will only appear when the container is empty.

  1. 3x Ale
  2. FoodCarrot
  3. AkaviriKatana
  4. Book1CheapWhiterun

Many thanks in advance for your ideas

Link to comment
Share on other sites

if you are using SKSE you can use this function on a container

https://www.creationkit.com/index.php?title=GetNumItems_-_ObjectReference

Scriptname CheckContainerScript extends ObjectReference

Message Property YourMessage Auto ;create one and fill the prop

Event OnItemRemoved(Form akBaseItem, int aiItemCount, ObjectReference akItemReference, ObjectReference akDestContainer)
   if self.GetNumItems() == 0
      YourMessage.show()
   endif
EndEvent

if you don'T want to use SKSE you have to workaround with native functions for example

https://www.creationkit.com/index.php?title=GetItemCount_-_ObjectReference

and use keywords and/or formlists as filters

Link to comment
Share on other sites

My main issue is probably that I can't figure out a chained condition that works as a single check for all items.

I don't want a message each time I remove one item; only show the message when the player got all 4 items from the container.

I'm looking for a custom solution without SKSE.

What are my options?

Edited by Uledus
Link to comment
Share on other sites

 

I don't want a message each time I remove one item; only show the message when the player got all 4 items from the container.

That is exactly what the if statement in my example does.

(if self.GetNumItems() == 0 ) tests if the container has exactly 0 items inside(aka empty) and ONLY THEN shows you the message.

In this method you don't need to worry about the amount or the type of items at all and don't need custom filters etc. You can change the items afterwards if you like without worrying about the script.

Link to comment
Share on other sites

If all you want to do is send that massage when everything is gone Testiger2's script will do that.

You could get a bit more complicated and ask if them items are gone. But, that script should work fine.

Maybe add a int flag on top and a if flag =0. Make the flag 1 1st time it is empty.

Int Flag = 0

 

 

 

Event OnItemRemoved(Form akBaseItem, int aiItemCount, ObjectReference akItemReference, ObjectReference akDestContainer)
if self.GetNumItems() == 0

if Flag== 0

Flag = 1
YourMessage.show()
endIf

endif
EndEvent

Edited by NexusComa
Link to comment
Share on other sites

Still no progress here, but thanks for your help so far guys!

I experimented with this type of scripts for almost two days without success.

I had a look again and compared your suggestions with my own attempts and script functionality is absolutely the same - it doesn't work though and I don't know the problem.

Another strange thing: On my first save the same script does other things than on my second save(I tested several containers in the Elsweyr cell).

On my first save, my message popped up every time after I selected an item in the container. On my second save, the message appeared once after I clicked an item in the container.

Afterwards I tested again with a new save, but the behaviour is still identical to the second save.

This has actually nothing to do with the desired behaviour(grab all items from the container and show the message).

What's wrong here? Any kind of help is highly appreciated - thanks!

Link to comment
Share on other sites

One, always test on a new game or a save that has not seen the mod in question. Script data tends to get "baked" into the save file once an object is loaded into memory. It is possible to have some changes go through while others do not. To avoid the hassle of determining if one's script edit fails to work as intended, always test on a new game or save that has not seen the mod in question or any of the objects used by the mod.

 

Non-SKSE method to check if all items were removed from a container?

 

Create a formlist that contains one entry for each base item added to the container. Then use this list to check the container. Example (theoretical, not tested)

 

 

Scriptname myContainerScript Extends ObjectReference
 
Formlist Property myContainerItems Auto
Message Property YourMessage Auto
 
Event OnItemRemoved(Form akBaseItem, int aiItemCount, ObjectReference akItemReference, ObjectReference akDestContainer)
  If CheckForEmptyContainer(myContainerItems) == true
    YourMessage.Show()
  EndIf
EndEvent
 
Bool Function CheckForEmptyContainer(Formlist Contents)
  Bool empty = true
  Int index = 0
  While index < Contents.GetSize()
    Form Entry = Contents.GetAt(index)
    If Entry.GetItemCount() > 0
      empty = false
      index = Contents.GetSize() ; only need to know if container is empty. no sense continuing loop if we found an item, set index so we can exit early
    EndIf
    index += 1
  EndWhile
  Return empty
EndFunction

What this does, when an item is removed from the container the function CheckForEmptyContainer is called. This function cycles through the passed in formlist and checks each entry. If just one item is still present the 'empty' bool is assigned the false value. Otherwise the true value for 'empty' is retained. In any event, the final value of 'empty' is passed on and used to determine whether or not your message can be shown.

 

 

Link to comment
Share on other sites

The problem with that approach is that it makes some assumptions: first, that the original contents are known and stored in the formlist, and, second, that nothing is subsequently added, both of which are likely to be false in most cases.

 

If the OP insists on a non-SKSE approach (SKSE is very much advised as it makes this much easier), I would:

 

1. Build a list of all contents in the container when the script starts. To do this, call a RemoveAllItems() on the chest you want to enumerate, dumping to a temporary chest, and then a RemoveAllItems() call on that chest, dumping back to the container of interest. You can then use OnItemAdded() on that container to insert items into a FormList via AddForm().

2. Add or remove items from that list as they are added or removed from the container. The same OnItemAdded() event that built the original list can be used for the added-items case (FormLists can't have duplicate forms in them, so it's safe to call AddForm without needing to check if the form in question is already present; AddForm will already do that for you), and you'll want to have an OnItemRemoved() event that checks whether the container still has any of that particular form. GetItemCount() is a non-SKSE function, fortunately. If it no longer has any of that form, you then remove it from the FormList. If the FormList hits size 0, register for a single update, with an arbitrarily short time; a tenth of a second or so should be fine. The update will only fire after the inventory menu gets closed because it ignores time spent in menus.

3. On the update firing, do another check of the FormList's size (in case, say, the player removed all the items, but then put some back) to make sure it is still 0. If it is, then you can display your message.

 

Aside from being somewhat kludgey and vulnerable to stack dumps if the container starts with a large number of different items, this method also has the issue that it will count and track unplayable (and hence invisible to the player) items, which some mods use as tracking tokens for various things. However every approach posted so far also has that vulnerability, and I believe that solving it requires the use of the SKSE function IsPlayable().

 

 

 

If you're up for SKSE, my approach would be to register for an update on the container being activated, and then on the update, use GetContainerForms() to dump the entire contents of the chest, after the player's closed out the menu, to a Form[] array. Then, I'd iterate through that array, checking for whether any given entry in it is playable, and, if it is, how many of them there are (this is important due to some stuff with how containers with leveled items work). If any of them are both playable and have a non-zero count you can break out and do nothing via a simple "return", and only display your message if the entire array was walked successfully.

If you're interested only in cases where the player has taken something from the container, and not ones where they've just opened an already-empty one, you can shift the RegisterForSingleUpdate() call into an OnItemRemoved() event instead of an OnActivated() one. Multiple RegisterForSingleUpdate() calls will not stack.

 

This approach is much more reliable and accurate and doesn't involve needing to move the items, which is the only way to build inventory lists in vanilla. It's also fast, since in normal use only a couple of items will need to be checked each time, and only when the container is closed, at that. I strongly recommend it over a strict non-SKSE solution.

Edited by foamyesque
Link to comment
Share on other sites

Put this script on the container (make sure you compile it)

If you don't use skse you should install it ...

 

Scriptname ContainerCheck extends ObjectReference
Int Flag = 0
Event OnInit()
GoToState("Done")
EndEvent
Event OnItemRemoved(Form akBaseItem, int aiItemCount, ObjectReference akItemReference, ObjectReference akDestContainer)
If(self.GetNumItems()==(0))
if(Flag==(0))
Flag=(1)
Debug.MessageBox("Items gone")
endIf
endif
EndEvent
;
State Done
;do nothing

EndState

Edited by NexusComa
Link to comment
Share on other sites

  • Recently Browsing   0 members

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