Hi! I'm having this weird issue with items added to a container in the Creation Kit not reacting properly to GetItemCount(). I'm using SKSE's GetNumItems() and GetNthForm() functions to scan the contents of a container in another script's OnInit() section and it gives me all the correct values (namely, the items I put into it), but the item counts all evaluate to 0. In fact, the item counts remain 0 until I add or remove an item ingame. How is this possible? I know for a fact that there are 5 items in that chest.
To provide a bit of background: I'm building a system of weapon displays for my player home that directly reflect the contents of a chest (or several chests). I created activators that look like the different weapons (referred to as "displays") and the idea is to enable/disable them based on whether the chest contains the respective weapon. These are the scripts I'm using (they're pretty long):
Abstract base for display scripts:
Spoiler Scriptname Saeri_DisplayItemScript extends ObjectReference{Display enabled/disabled depending on a specific item being present in a container. Abstract base script, extend for different item types}Message Property msg Auto{Message shown when player tries to activate the display (if take on activation is disabled)}Bool Property takeOnActivation = FALSE Auto Hidden{If enabled allows player to take item from container by activating the display}Form Property MyItem Auto Hidden{The item this activator represents, set by extending scripts}Int Property MyItemCount = 1 Auto{Item count necessary for the display to show up. DEFAULT : 1}FormList Property MyContainers Auto{The observed containers. The containers are added to formlist in the Creation Kit "by hand"}Function Init() ;Register for OnItemAdded and OnItemRemoved events with containers Int i = MyContainers.GetSize() Saeri_DisplayContainerScript currentContainer While(i > 0) i -= 1 currentContainer = MyContainers.getAt(i) As Saeri_DisplayContainerScript currentContainer.RegisterObserver(Self) InitialiseContainer(currentContainer) EndWhile ;Force display to evaluate container contents in case they are not empty (e.g. items added in the Creation Kit) ;--> This is NOT working, for some reason all item counts evaluate to zero Notify(MyItem)EndFunctionFunction InitialiseContainer(Saeri_DisplayContainerScript akContainer) ;Do nothing (placeholder for extending scripts)EndFunction;Called by OnItemAdded or OnItemRemoved events by the observed containers;Prompts the display to reevaluate the containers' contentsFunction Notify(Form akBase) If (akBase == MyItem) ;Only do anything if the item added/removed is of interest to this display GotoState("Notified") RegisterForSingleUpdate(0.1) EndIfEndFunction;Used for the TakeOnActivation feature that allows to take the represented item "off the shelf" by activating the display;Try to remove one instance of the represented item from one of the observed containers, return false if not foundBool Function TakeItem(ObjectReference akActor) Int i = MyContainers.GetSize() ObjectReference currentContainer Bool found = FALSE While(i > 0 && !found) i -= 1 currentContainer = MyContainers.getAt(i) As ObjectReference found = TakeItemFrom(currentContainer, akActor) EndWhile return foundEndFunction;Try to remove one instance of the represented item from akContainer. Returns false if none present.;Placeholder for extending scriptsBool Function TakeItemFrom(ObjectReference akContainer, ObjectReference akActor) return TakeThisItemFrom(MyItem, akContainer, akActor)EndFunction;Try to remove one instance of akBase from akContainer. Returns false if none present.Bool Function TakeThisItemFrom(Form akItem, ObjectReference akContainer, ObjectReference akActor) If(akContainer.GetItemCount(akItem) > 0) akContainer.RemoveItem(akItem, 1, FALSE, akActor) return TRUE Else return FALSE EndIfEndFunction;Used to decide if the display should be enabled or not;Returns the total count of the represented item in the observed containersInt Function TotalItems() Int total = 0 Int i = MyContainers.GetSize() ObjectReference currentContainer While(i > 0 && total < MyItemCount) i -= 1 currentContainer = MyContainers.getAt(i) As ObjectReference total += TotalInContainer(currentContainer) EndWhile return totalEndFunction;Returns the total count of the represented item in akContainer;Placeholder for extending scriptsInt Function TotalInContainer(ObjectReference akContainer) return akContainer.GetItemCount(MyItem)EndFunctionFunction Show() Enable()EndFunctionFunction Hide() Disable()EndFunctionEvent OnInit() Init()EndEvent;If TakeOnActivation is enabled, removes one instance of the represented item from an observed container and gives it to the player.;Otherwise, it displays a message boxEvent OnActivate(ObjectReference akActor) If(takeOnActivation) If(TakeItem(akActor)) ;Reevaluate the containers' contents, maybe the removed item was the last one Notify(MyItem) Else debug.notification("Something went wrong: " + Self + "should not be enabled!") Hide() EndIf Else msg.Show() EndIfEndEventState NotifiedFunction Notify(Form akBase) ;Do NothingEndFunction;Decides if the display should be enabled or not;Display is enabled if the represented item is present in the observed containers in a suitable quantityEvent OnUpdate() If (TotalItems() >= myItemCount) Show() Else Hide() EndIf GotoState("")EndEventEndState
Concrete display script for weapons (attached to the activators):
Spoiler Scriptname Saeri_DisplayWeaponScript extends Saeri_DisplayItemScript {Display activator: Weapon}Weapon Property MyWeapon Auto{The Weapon this activator represents}FormList Property Alternatives Auto{Alternative versions of represented weapon (e.g. enchanted)}Function Init() MyItem = MyWeapon ;Add represented item to formlist because it's easier to simply traverse one list than keeping both in mind If(!Alternatives.HasForm(MyItem)) Alternatives.AddForm(MyItem) EndIf TakeOnActivation = TRUE parent.Init()EndFunction;Scan the container for items added in the Creation Kit so they can be added to the alternatives list;(otherwise they are not recognised, since they didn't trigger onItemAdded events)Function InitialiseContainer(Saeri_DisplayContainerScript akContainer) Int i = akContainer.GetNumItems() Weapon akBase While(i > 0) i -= 1 akBase = akContainer.GetNthForm(i) As Weapon If(akBase && !Alternatives.HasForm(akBase) && IsAlternative(akBase)) debug.notification(akContainer.GetItemCount(akBase)) ;--> This is always 0, even though at this point the container MUST contain at least one of akBase! Alternatives .AddForm(akBase) EndIf EndWhileEndFunction;Returns true if argument is a viable substitute for the item represented by this display, e.g. an enchanted versionBool Function IsAlternative(Weapon akBase) return akBase.GetModelPath() == MyWeapon.GetModelPath()EndFunction;Called by OnItemAdded or OnItemRemoved events by the observed containers;Prompts the display to reevaluate the containers' contentsFunction Notify(Form akBase) If(Alternatives.HasForm(akBase)) ;Only do anything if the item added/removed is of interest to this display parent.Notify(MyItem) ElseIf(akBase && IsAlternative(akBase As Weapon)) ;Keep track of possible alternatives to MyItem Alternatives .AddForm(akBase) parent.Notify(MyItem) EndIfEndFunction;Try to remove one instance of the represented item (or one of its alternatives) from akContainer. Returns false if none present.Bool Function TakeItemFrom(ObjectReference akContainer, ObjectReference akActor) Bool found = FALSE Int i = Alternatives.GetSize() While(i > 0 && !found) i -= 1 found = TakeThisItemFrom(Alternatives.GetAt(i), akContainer, akActor) EndWhile return foundEndFunction;Returns the total count of the represented item (or one of its alternatives) in akContainerInt Function TotalInContainer(ObjectReference akContainer) return akContainer.GetItemCount(Alternatives)EndFunctionState Notified;Continue to keep track of possible alternatives to the represented itemFunction Notify(Form akBase) If(akBase && !Alternatives.HasForm(akBase) && IsAlternative(akBase As Weapon)) Alternatives .AddForm(akBase) EndIfEndFunctionEndState
Script attached to the container:
Spoiler Scriptname Saeri_DisplayContainerScript extends ObjectReference {Script for the observerd container}FormList Property Observers Auto{List of observers (display activators). The displays register themselves with the container in their OnInit() block.};Add akDisplay to the list of observersFunction RegisterObserver(Saeri_DisplayItemScript akDisplay) If(akDisplay && !Observers.HasForm(akDisplay)) Observers.AddForm(akDisplay) EndIf ;--> Tried to resolve the issue with Editor-defined container content by adding/removing the display's item ; This does NOT work, the item is added but not removed for some reason, and the display still doesn't react to the content ;AddItem(akDisplay.MyItem, 1) ;RemoveItem(akDisplay.MyItem, 1)EndFunction;Notify displays of items added/removed to/from the container;The item is only passed so the display can filter out changes that cannot actually affect it (for efficiency);Notify promts the display to reevaluate the entire content of all its observed containers (less error prone)Function NotifyObservers(Form akBase) Saeri_DisplayItemScript akDisplay Int i = Observers.GetSize() While (i > 0) i -= 1 akDisplay = Observers.GetAt(i) As Saeri_DisplayItemScript akDisplay.Notify(akBase) EndWhileEndFunctionEvent OnItemAdded(Form akBaseItem, int aiItemCount, ObjectReference akItemReference, ObjectReference akSourceContainer) NotifyObservers(akBaseItem)EndEventEvent OnItemRemoved(Form akBaseItem, int aiItemCount, ObjectReference akItemReference, ObjectReference akDestContainer) NotifyObservers(akBaseItem)EndEvent
As far as I can tell they work as expected if the observed containers are all empty at initialisation. They react appropriatly to items added to or removed from the chests at runtime. What I'd like them to do is reflect the contents of a container that I set up with a few low-value items, so I can preview the system without the player needing to touch anything first. But I cannot get it to work, since for some reason my scripts completely ignore any items added to the container in the CK prior to the moment the player adds or removes an item for the first time.
Is this common behaviour of containers? Is there some initialisation procedure I'm missing? I apologise if this is a well-known issue, but I couldn't find anything about it in the official Wiki. Also, general advice on my scripts would be welcome, I'm not entirely sure I'm doing this in the most efficient manner. Actually I'm amazed I got it to (partially) work at all 