Jump to content

need help with part of my script, plz and thx :)


Recommended Posts

so I have this script which seems to be working perfectly other than one small part, which I hope someone can point out a solution to. The script is attached to a chest which is link chained to various real weapon displays on the wall above it. When you dump an item in it queues up a single update which fires the function to check the contents against the linked chain of displays and compares the model paths, and if they match, the display is enabled. The problem I am having is the code to disable the displays. If you remove them one by one, the code as it is written now works fine, but if you remove them all at once or rapidly spam the take button it causes the OnItemRemoved() queue hold up where it drops random functions, hence leaving stuff on the wall. I'm having a hard time wrapping my head around how to do a nested pair of while loops (like the item added option has) because obviously the items are no longer in the container to compare with any longer, and the standard code of monitoring the item as it leaves and comparing it causes the issues at hand.

 

Would setting up a blank formlist and having the additem function add the base item form reference to the formlist and then have the removeitem update event function fire and check the form list against the container and disable any linked chain item that has a matching model which has 0 count in the container and then removing said entry from the list be a plausible solution? seems a bit convoluted to me, but I can't think of another method

 

 

Scriptname DD_WeaponArmorDisplayScript extends ObjectReference  

Form[] BID
ObjectReference LR
ObjectReference OLR
Int AddState
Weapon MyWeapon
String ItmFilePath
String DFilePath
Weapon LRW
int ITMCNT

Event OnInit()
    Int i = countLinkedRefChain()
    BID = Utility.CreateFormArray(i)
    While i
        i -= 1
        BID[i] = GetNthLinkedRef(i).GetBaseObject() As Form
    EndWhile
EndEvent

Event onLoad()
    Int i = BID.Length
    While i > 1
        i -= 1
        LR = GetNthLinkedRef(i)
     if (Self.Is3DLoaded())
        LR.BlockActivation(true)
        LR.setMotionType(Motion_Keyframed, FALSE)
        LR.MoveToMyEditorLocation()
     Endif
    EndWhile
EndEvent

Event onCellAttach()
    Int i = BID.Length
    While i > 1
        i -= 1
    if (Self.Is3DLoaded())
        LR = GetNthLinkedRef(i)
        LR.BlockActivation(true)
        LR.MoveToMyEditorLocation()
    Endif
    EndWhile
EndEvent

Event onLoadGame()
    Int i = BID.Length
    While i > 1
        i -= 1
    if (Self.Is3DLoaded())
        LR = GetNthLinkedRef(i)
        LR.MoveToMyEditorLocation()
        LR.BlockActivation(true)
        LR.setMotionType(Motion_Keyframed, FALSE)
      EndIf
    EndWhile
EndEvent

Event OnItemAdded(Form akBaseItem, int aiItemCount, ObjectReference akItemReference, ObjectReference akSourceContainer)
if akSourceContainer == Game.getPlayer()
    RegisterforSingleUpdate(1)
    debug.notification("Update registered")
    AddState = 1
endif
EndEvent

Event OnItemRemoved(Form akBaseItem, int aiItemCount, ObjectReference akItemReference, ObjectReference akDestContainer)
if akDestContainer == Game.GetPlayer()
    MyWeapon = akBaseItem as weapon
    RegisterforSingleUpdate(1)
    debug.notification("Update registered")
;    AddState = 2
RemoveDisplays()
endif
EndEvent

Event OnUpdate()
    debug.notification("Event Updated")
    if AddState == 1
        AddDisplays()
        AddState = 0
    Elseif AddState == 2
        RemoveDisplays()
        AddState = 0
    Endif
EndEvent

Function AddDisplays()
    debug.notification("Add display fired")
        Int i = BID.Length
        While i > 1
            i -= 1
            LR = GetNthLinkedRef(i)
            LRW = LR.GetBaseObject() as Weapon
           OLR = GetNthLinkedRef(i +1)
         ITMCNT = GetNumItems()
    While ITMCNT
        ITMCNT -= 1
        MyWeapon = GetNthForm(ITMCNT) as weapon
    
    DFilePath = LRW.GetModelPath()
    ItmFilePath = MyWeapon.GetModelPath()
            If DFilePath == ItmFilePath
    debug.notification("Model paths match")
                If LR.IsDisabled()
                    LR.Enable()
                EndIf
            EndIf
    EndWhile
        EndWhile
        onLoadGame()
EndFunction

Function RemoveDisplays()
    debug.notification("remove display fired")
    If BID.Find(MyWeapon) >= 1 && GetItemCount(MyWeapon) == 0
        Int i = BID.Length
        While i > 1
            i -= 1
            LR = GetNthLinkedRef(i)
            If LR.GetBaseObject() == MyWeapon
                If LR.IsEnabled()
                    LR.Disable()
                EndIf
            EndIf
        EndWhile
    EndIf
EndFunction
Link to comment
Share on other sites

Lol, I have the answer, but i don't want to rewrite it from scratch again.

 

A few simple things you can do to catch the removed items (won't catch them all).

 

Use GetNumItems() on the container.

If 0 then disable whole chain.

There is actually a DisableLinkChain function already supplied so you don't have to write one:

http://www.creationkit.com/DisableLinkChain_-_ObjectReference

 

This will catch when a user hits "Take All"

where you implement GetNumItems() checks is up to you.

But GetNumItems() is a quick returning function so calling checks regularly shouldn't be to consuming.

(Maybe when exiting the container do a quick check and disable as needed or each time you get a removeitem event even wouldn't hurt)

 

If you want to be a little less dynamic (eg: customize the script for just this instance of use), then use keywords.

Add your keywords as properties or property array (my preference)

It'll make it so you can check the container via keyword and disable the link chain by the the same keyword.

 

eg: GetItemCount(Keywrord)

will quickly return the count of items in the container with that keyword, if 0 then DisableLinkChain(keywword)

 

The more I look at what you've done in that script the more I see your double handling things.

eg:

LR = GetNthLinkedRef(i)
LRW = LR.GetBaseObject() as Weapon

 

You already have an array of the base objects in the BID array.

The BID array indexes are at the same indexes of the linked ref chain.

eg;

LR = GetNthLinkedRef(i)
LRW = BID as Weapon

 

You have lost all focus on what's what.

I saw your worn form function the other day and I can't believe how nice that function you ended with was.

Then i see the above script and I'm like damn, the same person did not do this...lol

 

 

Edit (Not related to your problem):

Also those Self.Is3dLoaded() will be causing papyrus spam if the item your setting motion type on isn't loaded, since Self is the Container (your not setting motion type on the container).

Your needing to check if the item your setting motion type is loaded eg: LR.Is3DLoaded()

(I actually did the same mistake and corrected it when I updated it last time)

I can see you were trying to save going any further if the container 3d isn't loaded, but you also look that some of those chained references aren't enabled (no 3d) while the container is loaded, so you've skipped the check and it will spam the log.

 

If it is a case of your wanting not to process any further if the container isn't loaded, then I'd suggest move the Self.Is3Dloaded() outside the while loop.

This way if the container isn't loaded the while loop never fires.

But regardless you still need to check the item chain linked ref 3d is loaded in the while loop.

eg:

Event onCellAttach()
    ResetDisplay()
EndEvent

Event onLoad()
    ResetDisplay()
EndEvent

Event onLoadGame()
    ResetDisplay()
EndEvent

Function ResetDisplay()
    If Self.Is3DLoaded()
        Int i = BID.Length
        While i > 1
            i -= 1
            LR = GetNthLinkedRef(i)
            If LR.Is3DLoaded()
                LR.BlockActivation(true)
                LR.setMotionType(Motion_Keyframed, FALSE)
                LR.MoveToMyEditorLocation()
            EndIf
        EndWhile
    EndIf
EndFunction

Edit Again:

had a look to see if I could refine that mess of a script a little.

 

I'm assuming your linked chain is changing models on the chain ?

Which seems odd when your adding as I thought you'd be using hard set display models (same base id as the form your adding)

Otherwise you may as well not give a rats about the disaplay model path and when adding a weapon you would set the display model path of the weapon your adding regardless of the weapon (which is not what your doing).

 

So what actually are you doing, that you have to check model paths when adding?

 

Anyways here's the same thing as you had (untested, but does compile).

It's based on checking the model path when adding and removing.

Should enable or disable on the amount you have available of a type in the chain.

 

But to be honest you really need to be a bit more informative as to exactly as to what your trying to accomplish.

Yeah I can see a base of it, but i had to assume to much and some of what your doing makes no sense (to me) in the way it is atm.

 

 

Scriptname DD_WeaponArmorDisplayScript extends ObjectReference

Form[] BID
ObjectReference LR

Event OnInit()
    Int i = countLinkedRefChain()
    BID = Utility.CreateFormArray(i)
    While i
        i -= 1
        BID[i] = GetNthLinkedRef(i).GetBaseObject() As Form
    EndWhile
EndEvent

Event onCellAttach()
    ResetDisplay()
EndEvent

Event onLoad()
    ResetDisplay()
EndEvent

Event onLoadGame()
    ResetDisplay()
EndEvent

Event OnItemAdded(Form akBaseItem, int aiItemCount, ObjectReference akItemReference, ObjectReference akSourceContainer)
    If (akBaseItem As Weapon)
        Int i
        Int j
        While i < aiItemCount
            j = 1
            While j < BID.Length
                LR = GetNthLinkedRef(j)
                If (BID[j] As Weapon).GetModelPath() == (akBaseItem As Weapon).GetModelPath() && LR.IsDisabled()
                    LR.Enable()
                    j = BID.Length
                EndIf
                j += 1
            EndWhile
            i += 1
        EndWhile
        ResetDisplay()
    Else
        ;not a weapon retuurn the akBaseItem of aiItemCount back to the player.
    EndIf
EndEvent

Event OnItemRemoved(Form akBaseItem, int aiItemCount, ObjectReference akItemReference, ObjectReference akDestContainer)
    If GetNumItems() == 0
        Int i = 1
        While i < BID.Length
            GetNthLinkedRef(i).Disable()
            i += 1
        EndWhile
    Else
        Int i
        Int j
        While i < aiItemCount
            j = 1
            While j < BID.Length
                LR = GetNthLinkedRef(j)
                If (BID[j] As Weapon).GetModelPath() == (akBaseItem As Weapon).GetModelPath() && LR.IsEnabled()
                    LR.Disable()
                    j = BID.Length
                EndIf
                j += 1
            EndWhile
            i += 1
        EndWhile
    EndIf
EndEvent

Function ResetDisplay()
    If Self.Is3DLoaded()
        Int i = BID.Length
        While i > 1
            i -= 1
            LR = GetNthLinkedRef(i)
            If LR.Is3DLoaded()
                LR.BlockActivation(true)
                LR.setMotionType(Motion_Keyframed, FALSE)
                LR.MoveToMyEditorLocation()
            EndIf
        EndWhile
    EndIf
EndFunction

 

 

The above still won't sync when removing multiple of the same items.

Basically say you have 3 of the same type in the chain and you have 10 item of the the same type in the container.

When removing only 3 from the container the 3 dispaly items will disable, but you'd still have 7 left in the container.

 

Are you limititing what you can add to the container so it's only the same amount that can be displayed?

(This is along the lines of being more informative that I referred to earlier)

Link to comment
Share on other sites

thanks for trying to help, but I'll try and be more clear.

 

Firstly the is3Dloaded() on the Self was an oversight, it was supposed to be on the LR as the condition, so thanks for pointing that out.

 

as far as what It's doing overall, it's really quite simple. You place a weapon in a chest and if there is a matching LR in the chain with the same model path, it enables the LR. Likewise when you remove the item from the chest, it will disable the LR with the same model. The reason for the models rather than using a form ID or keywords or base type is it's set to allow display of the item based on what it looks like rather than what the item IS. So if you have an enchanted iron sword of some kind it will display the base iron sword if you plop it in. It's also got proxy weapons set up on the chain for a variety of expanded weaponry and rather than using getformfromfile which varies extensively, the model paths between a few mods that utilize said items are fairly consistent.

 

At any rate, I haven't looked at papyrus yet to debug, but everything in the script seems to be functioning properly like I said other than the removal routine. I am going to just do a simple RegisterForSingleUpdate() that checks if the container is empty and have it disable all of the items on the chain manually that way, and see if quick removal of individual items (all but one) behaves or still has queue loss.

 

Your script above is actually ignoring the key problem I posted about in my OP, which is that having multiple OnItemAdded() or OnItemRemoved() functions back to back causes the system to queue the events and some get dumped and items get ignored. You had this same problem in the example script you made for the liquor bottles. If you dumped all 14 items or whatever into it too quickly or remove them all too fast it would miss enabling and disabling random references on the chain. The RegisterForSingleUpdate() event solved this problem (hence why I put it in) so that the actual check for matching references occurred only once after the menu was closed, that way it's all one while loop check that isn't being interrupted by another OnItemAdded/Removed() event. The multiple Re-registrations of multiple updates back to back causes no issues because registering for a single update merely kicks the previous registration out as opposed to RegisterForUpdate() which just stacks them up.

 

So like I said in the OP, the only part that really has me vexed at this point is trying to accurately pass removed items through to disable the item on the chain, but I'll get there. I'm sure that my solution will probably still miss parts on fast removal of successive items still but I'll probably figure out a way around it.

Link to comment
Share on other sites

Do it like the bookcase script.

Event OnActivate()

Utility.Wait(0.1)
UpdateYourDisplayAsTheMenuIsClosedIfI'mCalled()

EndEvent

Not tested, but something like this (only updates after exiting the container, not while in the container):

 

Scriptname DD_WeaponArmorDisplayScript extends ObjectReference

;Swapped BID for MP string array since your not using base id to compare.
String[] MP
ObjectReference LR
Bool bChange

Event OnInit()
    Int i = countLinkedRefChain()
    MP = Utility.CreateStringArray(i)
    While i
        i -= 1
        MP[i] = (GetNthLinkedRef(i).GetBaseObject() As Weapon).GetModelPath()
    EndWhile
EndEvent

Event onCellAttach()
    ResetDisplay()
EndEvent

Event onLoad()
    ResetDisplay()
EndEvent

Event onLoadGame()
    ResetDisplay()
EndEvent

Event OnActivate(ObjectReference akActionRef)
    bChange = False
    Utility.Wait(0.1)
    UpdateDisplay()
EndEvent

; Add / Remove Only used to notify change, if no change then don't Update the display
Event OnItemAdded(Form akBaseItem, int aiItemCount, ObjectReference akItemReference, ObjectReference akSourceContainer)
    bChange = True
EndEvent

Event OnItemRemoved(Form akBaseItem, int aiItemCount, ObjectReference akItemReference, ObjectReference akDestContainer)
    bChange = True
EndEvent

;may want to block activation while updating, so the user can't activate the container till it finishes updating.
Function UpdateDisplay()
    If bChange
        ;Disable all display items, not even checking if they're enabled.
        Int i = MP.Length
        While i > 1
            i -= 1
            GetNthLinkedRef(i).Disable()
        EndWhile

        ;Loop through container and set the display items as needed, also return any non weapon items we find back to the player.
        Form CurForm
        Int iCnt
        Int j
        i = GetNumItems()
        While i
            i -= 1
            CurForm = GetNthForm(i)
            If (CurForm As Weapon)
                iCnt = GetItemCount(CurForm)
                j = 1
                While ((j < MP.Length) && iCnt)
                    LR = GetNthLinkedRef(j)
                    If (MP[j] == (CurForm As Weapon).GetModelPath()) && LR.IsDisabled()
                        LR.Enable()
                        iCnt -= 1
                    EndIf
                    j += 1
                EndWhile
            Else
                ;Not weapon give it back to the player
                Self.Removeitem(CurForm, GetItemCount(CurForm), True, Game.GetPlayer())
            EndIf
        EndWhile
        ResetDisplay()
    EndIf
EndFunction

Function ResetDisplay()
    Int i = MP.Length
    While i > 1
        i -= 1
        LR = GetNthLinkedRef(i)
        If LR.Is3DLoaded()
            LR.BlockActivation(true)
            LR.setMotionType(Motion_Keyframed, FALSE)
            LR.MoveToMyEditorLocation()
         EndIf
    EndWhile
EndFunction

 

 

Link to comment
Share on other sites

Well your script you spoilered above seems to enable and disable the items reliably and quickly, but it does not do it accurately. It seems to be mis-matching the models of the current reference and the current item and I can't for the life of me see why. So it will in effect enable and disable incorrect models almost as if one of the variables is not getting reset before running the whole check function again? and instead of starting again, it starts from where it left off, but that just doesn't make sense to be because they are conditioned to not do anything if they aren't matched.

 

I'm not concerned about multiples of the same object on the chain on this, as there are only one of each item in the chain, I'm more concerned with it simply disabling when there are zero items matching the model, that's pretty much it. I think my brain is about to implode, I've been looking at this too long and been way too close to success and now everything I try seems to just make it worse :P

Link to comment
Share on other sites

Based on sLoPpYdOtBiGhOlE's ideas here's my version.

It makes use of the Find function on an array of model paths and ensures items aren't missed by keeping of list of which display items should be active which gets updated completely whenever items are added or removed from the container. A Busy state ensures that you don't get quirky behavior if you try to add or remove items while the script is trying to refresh the item display.

 

It compiles and I think it should be right, but I haven't actually run it so there might be errors.

ScriptName WeaponDisplayScript extends ObjectReference  

int DispCount		; Number of linked display items
bool[] DispActive	; Tracks which should be displayed
string[] DispModel	; Searchable array of the model paths 

Event OnInit()
	DispCount = CountLinkedRefChain()
	DispActive = Utility.CreateBoolArray(DispCount)
	DispModel = Utility.CreateStringArray(DispCount)
	int i = DispCount
	While i > 1  ; item 0 is unused because it is the container itself
		i -= 1
		DispModel[i] = (GetNthLinkedRef(i).GetBaseObject() As Weapon).GetModelPath()
	EndWhile
EndEvent

Event OnLoad()
	RefreshDisplay()
EndEvent

Event OnCellLoad()
	RefreshDisplay()
EndEvent

Event OnCellAttach()
	RefreshDisplay()
EndEvent

Event OnItemAdded(Form akBaseItem, int aiItemCount, ObjectReference akItemReference, ObjectReference akSourceContainer)
	RegisterforSingleUpdate(0.1)
EndEvent

Event OnItemRemoved(Form akBaseItem, int aiItemCount, ObjectReference akItemReference, ObjectReference akDestContainer)
	RegisterforSingleUpdate(0.1)
EndEvent

; if changes are made while an update is in progress another will run later to catch the new changes
State Busy
	Event OnUpdate()
		RegisterforSingleUpdate(0.5)
	EndEvent
EndState

; determine which items should be displayed based on items currently in the container
Event OnUpdate()
	RefreshDisplay(true)
EndEvent

Function RefreshDisplay(bool fullRefresh = false)
	GoToState("Busy")
	int i
	if fullRefresh
		; assume none should be displayed
		i = DispCount
		While i > 1
			i -= 1
			DispActive[i] = false
		EndWhile
		; mark for display if any item in the container is a match for the model path
		i = GetNumItems()
		While i
			i -= 1
			Weapon item = GetNthForm(i) as Weapon
			If item
				int match = DispModel.Find(item.GetModelPath())
				If match > 0
					DispActive[match] = true
				EndIf
			EndIf
		EndWhile
	EndIf
	; enable and disable the display items
	i = DispCount
	While i > 1
		i -= 1
		ObjectReference LR = GetNthLinkedRef(i)
		LR.BlockActivation(true)
		if DispActive[i]
			If LR.IsDisabled()
				LR.Enable()
			EndIf
			Utility.Wait(0.1) ; give extra time for the item 3d to load completely
			LR.SetMotionType(Motion_Keyframed, false)
			LR.MoveToMyEditorLocation()
		Else
			If !LR.IsDisabled()
				LR.Disable()
			EndIf
		EndIf
	EndWhile
	GoToState("")
EndFunction

P.S. There is no onLoadGame() event. What that a custom event or were you thinking of OnCellLoad()?

Link to comment
Share on other sites

Well your script you spoilered above seems to enable and disable the items reliably and quickly, but it does not do it accurately. It seems to be mis-matching the models of the current reference and the current item and I can't for the life of me see why. So it will in effect enable and disable incorrect models almost as if one of the variables is not getting reset before running the whole check function again? and instead of starting again, it starts from where it left off, but that just doesn't make sense to be because they are conditioned to not do anything if they aren't matched.

 

I'm not concerned about multiples of the same object on the chain on this, as there are only one of each item in the chain, I'm more concerned with it simply disabling when there are zero items matching the model, that's pretty much it. I think my brain is about to implode, I've been looking at this too long and been way too close to success and now everything I try seems to just make it worse :tongue:

 

Hmm, odd, maybe 2 things maybe causing it iCnt or returning items to the player..

I removed icnt since your not phased about the count per item in the container matching multiple of the same in the chain as there is only 1 of each in the chain..

I ditched the Loop for CurForm and used find as it's quicker if it's only 1 in the chain.

Added Busy State when updating so the user can't activate while updating.

Moved LR to local in functions instead of global, should stop some possible probs in some certain circumstances.

TBH I really should do a test on it and I could probably nail it pretty quick.

Just CK irks me some most days and I avoid it like the plague...lol

 

Scriptname DD_WeaponArmorDisplayScript extends ObjectReference

String[] MP

Event OnInit()
    Int i = countLinkedRefChain()
    MP = Utility.CreateStringArray(i)
    While i
        i -= 1
        MP[i] = (GetNthLinkedRef(i).GetBaseObject() As Weapon).GetModelPath()
    EndWhile
EndEvent

Event onCellAttach()
    ResetDisplay()
EndEvent

Event onLoad()
    ResetDisplay()
EndEvent

Event OnActivate(ObjectReference akActionRef)
    BlockActivation()
    Utility.Wait(1.0)
    UpdateDisplay()
    BlockActivation(False)
EndEvent

Function UpdateDisplay()
    GoToState("Busy")
    ObjectReference LR
    Int i = MP.Length
    While i > 1
        i -= 1
        GetNthLinkedRef(i).Disable()
    EndWhile
    Form CurForm
    Int j
    i = GetNumItems()
    While i
        i -= 1
        CurForm = GetNthForm(i)
        If (CurForm As Weapon)
            j = MP.Find((CurForm As Weapon).GetModelPath())
            If j >= 1
                LR = GetNthLinkedRef(j)
                If LR.IsDisabled()
                    LR.Enable()
                EndIf
            EndIf
        Else
            Self.Removeitem(CurForm, GetItemCount(CurForm), True, Game.GetPlayer())
        EndIf
    EndWhile
    ResetDisplay()
    GoToState("")
EndFunction

Function ResetDisplay()
    ObjectReference LR
    Int i = MP.Length
    While i > 1
        i -= 1
        LR = GetNthLinkedRef(i)
        If LR.Is3DLoaded()
            LR.BlockActivation(true)
            LR.setMotionType(Motion_Keyframed, FALSE)
            LR.MoveToMyEditorLocation()
         EndIf
    EndWhile
EndFunction

State Busy
    Event OnActivate(ObjectReference akActionRef)
        ;updating do nothing
    EndEvent
EndState

 

 

 

Have edited this above code like 5 or maybe 10 times since posting lol

 

TBH I would just update the display after exiting the container menu.

Even if it's not as pretty as doing it while in the container menu.

You could use register for menu and catch the container menu when exiting to update display, this way your not holding the OnActivate event causing weird behavior if needed.

 

Edit Again + 25:

Tested the above script and it works for me, quite quickly as well I might add.

Makes no diff if I remove all or juggle stuff while in the container, as soon as I exit the container it updates correctly for me.

Ditched the OnItemAdd/Remove event bs altogether as they are unreliable to base updates on.

So even if you don't add or remove items when in the container menu the chain is updated when exiting.

Drawback is the disable and enable showing when exiting the container.

But meh, it does what it's meant to.

 

Put 6 different weapons on a wall, chained from the chest attached script and each fires as it's meant to.

Can post my test esp but I'm sure I don't need to.

Link to comment
Share on other sites

Oh man, you are a god! that works perfectly, I can't believe I didn't just set the remove item check to re-poll what was in the container, that's a stroke of genius (and something obvious I should have thought about lol). Thanks so much for clearing this up for me, I and Saerileth greatly appreciate it (and the fans of Druid's Den will be grateful as well, which I'm working on this for). Now I can actually move on to fixing up other stuff :) take care!

Link to comment
Share on other sites

Yeah it's funny how the most obvious tactic (just disabling everything and re-enabling stuff that matches the container items) is the one that escapes us for awhile. Glad it occurred to you though :)

 

I appreciate all the help, between this script and reading up a little more I understand building linked arrays much better now.

Link to comment
Share on other sites

  • Recently Browsing   0 members

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