Need help with InventoryEventFilter and script logic

Post » Wed Jun 20, 2012 3:00 pm

Here's what I want to do:

I want to add a script to the Player. If he picks up a piece of armor, that particular armor is removed from the inventory and is replaced with a different armor. This is applied to every single piece of playable armor, excluding accessories and circlets

Okay, so I'm planning on adding an OnItemAdded Event to player. Then I'll cycle through theFormList and compare the added item argument with each object in the FormList. Is it good enough? n order to maximize efficiency, I add InventoryFilter with that FormList as the argument

Here's the piece of code:
Scriptname RaestlozArmorChangerPlayerScript extends ReferenceAlias{Script that runs on Player.Changes the instance of armor that player gets}FormList Property VanillaArmorList AutoFormList Property CustomArmorList Autoint TempIndex = 0Event OnInit()   AddInventoryEventFilter(VanillaArmorList)endEventEvent OnItemAdded(Form akBaseItem, int aiItemCount, ObjectReference akItemReference, ObjectReference akSourceContainer)   TempIndex = GetFormIndex(VanillaArmorList, akBaseItem)   if ( TempIndex > -1 )   Game.GetPlayer().removeItem(akBaseItem, aiItemCount,true);   Game.GetPlayer().addItem(CustomArmorList.GetAt(TempIndex), aiItemCount,true);   endifendEventint Function GetFormIndex(FormList List, Form Member) global{Gets the index of Member in List. Returns -1 if not found}   if (!List.HasForm(Member))      Return -1   endifint Index = 0   While (List.GetAt(Index) != Member)      Index += 1   EndWhile   Return IndexEndFunction

What I need to know is, will this bog down script performance? For reference, Daedric Armor set alone consists of at least 100 items (the enchantment variant is to blame). I expect the total to be at least 700-900 items, perhaps more. This check will be performed everytime the player obtains an armor.

Should I opt with something else instead? Like, say, OnContainerChanged?

Now, I read in CreationKit.com that I should use InventoryEventFilter for this work, to... IDK, it simply says it's better to use it.

How do I use it? I know it says I can pass a FormList as an argument, but what exactly does it do?

If I want each item in the FormList to be treated differently, should I even use a FormList?

If I do use a FormList, it will contain at least 100 entries. Is it possible to see at which index of FormList A an item is and pick an item in FormList B at the same index?

EDIT: I just found out about FormList.GetAt(). It certainly helps, but that alone won't exactly help me for the core operations
User avatar
CRuzIta LUVz grlz
 
Posts: 3388
Joined: Fri Aug 24, 2007 11:44 am

Post » Wed Jun 20, 2012 8:23 pm

The NN01CrimsonNirnrootScript.psc script shows a good example of using an Inventory Event Filter.

That's an AliasReference script that, presumably, gets attached to the player during the Crimson Nirnroot quest.

All it has essentially is the OnItemAdded/Removed events to count the Nirns, but it uses the Event filter so that those events are only called when the subject item is a Nirn. So that's why when you are only looking for particular items it's worth using the Event Filter. As it is a built in feature, It's likely going to be much faster than implementing the same in Papyrus.

In this case though, as they will be all Armor and it looks like your mod is covering a lot of the ingame stuff then using your own filter of...
Event OnItemAdded(Form akBaseItem, int aiItemCount, ObjectReference akItemReference, ObjectReference akSourceContainer)  If (akBaseForm As Armor)	;stuff  EndIfEndEvent
...might be an option worth testing. :shrug:

Using 2 formlists as a lookup table works well.
User avatar
(G-yen)
 
Posts: 3385
Joined: Thu Oct 11, 2007 11:10 pm

Post » Thu Jun 21, 2012 4:55 am

The NN01CrimsonNirnrootScript.psc script shows a good example of using an Inventory Event Filter.

That's an AliasReference script that, presumably, gets attached to the player during the Crimson Nirnroot quest.

All it has essentially is the OnItemAdded/Removed events to count the Nirns, but it uses the Event filter so that those events are only called when the subject item is a Nirn. So that's why when you are only looking for particular items it's worth using the Event Filter. As it is a built in feature, It's likely going to be much faster than implementing the same in Papyrus.

In this case though, as they will be all Armor and it looks like your mod is covering a lot of the ingame stuff then using your own filter of...
Event OnItemAdded(Form akBaseItem, int aiItemCount, ObjectReference akItemReference, ObjectReference akSourceContainer)  If (akBaseForm As Armor)	;stuff  EndIfEndEvent
...might be an option worth testing. :shrug:

Using 2 formlists as a lookup table works well.
I'll look into it. Many thanks for the advice

I need to be able to distinguish every single piece of vanilla armor, including each leveled variant and excluding the non-playable ones (oh, and excludes the accessories too, so not THAT many, but still VERY many)

Which is why I'm spinning my head trying to optimize the whole ordeal.

Currently, I'm creating a dummy quest which attaches to the player a script. That Crimson Nirnroot pretty much gets me off the ground. I just have to find a way to iterate through the whole FormList and distinguish the content

I'm thinking of something along these lines:
int iLooper = 0;Event OnItemAdded(Form akBaseItem, int aiItemCount, ObjectReference akItemReference, ObjectReference akSourceContainer)   for(iLooper = 0;iLooper < VanillaArmorFormList.length; iLooper++)      if akBaseItem == VanillaArmorFormList.getAt(iLooper) as ObjectReference         Game.GetPlayer().removeItem(akBaseItem,1,true);         Game.GetPlayer().addItem(ModArmorFormList.getAt(iLooper),1,true);        break;      endIf   endForEndEvent

Of course, I just pulled that pseudo-code out of my *beep*, my background is ActionScript 3.0, and I find Papyrus (and Creation Kit as a whole) to be extremely not straight-forward and full of loops just to make a simple goddang armor.

It amazes me how their developers can get away with this bullcrap called "Armor AddOn", which effectively doubles the amount of work to be done with half the productivity, so I guess it's like a crappy client?

Back on topic: will that work? Do Papyrus even have "For"? and does it even have "break"?
User avatar
Dalia
 
Posts: 3488
Joined: Mon Oct 23, 2006 12:29 pm

Post » Wed Jun 20, 2012 4:31 pm

There's a sample GetFormIndex function on the http://www.creationkit.com/GetAt_-_FormList() wiki page, which pretty much does what you are doing there in the for( ).
Papayrus only has while(), and in a function you can use Return in place of Break.

int got = GetformIndex(FormListA, akBaseItem)if got != -1  Armor new = FormListB.GetAt(got)endif

When you replace the armour with the new model, I'm pretty sure that will cause another event to fire.
So it might be worth using the Event Filter after all with VanillaArmorFormList. Assuming formListB doesn't contain any vanilla stuff.

I've only ever used Armor myself as tokens, so I've avoided all the bullcrap :)
User avatar
Esther Fernandez
 
Posts: 3415
Joined: Wed Sep 27, 2006 11:52 am

Post » Wed Jun 20, 2012 3:07 pm

There's a sample GetFormIndex function on the http://www.creationkit.com/GetAt_-_FormList() wiki page, which pretty much does what you are doing there in the for( ).
Papayrus only has while(), and in a function you can use Return in place of Break.

int got = GetformIndex(FormListA, akBaseItem)if got != -1  Armor new = FormListB.GetAt(got)endif

When you replace the armour with the new model, I'm pretty sure that will cause another event to fire.
So it might be worth using the Event Filter after all with VanillaArmorFormList. Assuming formListB doesn't contain any vanilla stuff.

I've only ever used Armor myself as tokens, so I've avoided all the bullcrap :smile:
*enlarges eyes in awe* Talos! You're a lifesaver! It saves me SO MUCH of headache!

I didn't even think about that! Many thanks!

Now, I just have to duplicate each armor twice :dry:

Good for you if you don't have to deal with these.... AddOns. I seriously thought "Cool! Plugins for armors! Can I attach a jetpack or something?" and it turns out it's just Bethesda's Liquid Graphics for "parts"
User avatar
[ becca ]
 
Posts: 3514
Joined: Wed Jun 21, 2006 12:59 pm

Post » Wed Jun 20, 2012 10:39 pm

Updated the main post. I need advice regarding performance for my script. Especially since the FormList will be HUGE. Like, HUGE.
User avatar
Michelle Smith
 
Posts: 3417
Joined: Wed Nov 15, 2006 2:03 am

Post » Thu Jun 21, 2012 6:17 am

There's be some minor things that can be changed, but it's hardly worth it right now until you've finished the working script.

The formlist will be the bottleneck at 2 points.

1/ When ANY item is added, the engine will check to see if the OnItemAdded needs to be called. If you notice a slow-down when you pick up say, a weapon, then try the
If !(akBaseForm As Armor)  ReturnEndIf

2/ GetformIndex()
This will hopefully (if filters are fast enough) only be run when the item is actually in the list.

The only way I can think of to make this faster is to search an ordered list.
That would mean adding all the Armor items into the formlist in FormID order. (not that hard to do in the CK)
Then with the FormID of the added armor a binary search can be done. (that's starting to look in the middle of the list, and throwing away the half that will not contain the target form, etc.)
The average iterations for a linear search would be 450, for a binary search the maximum iterations would be ~10

So I don't think the final version is going to be as laggy as you are dreading.
Do some stress tests first with a big vanilla formlist and just print the FormID, and then another test where you go on to find the index of the added armor.
The 2nd one we know can be speeded up.

Edit: Add Binary Search Method.
Spoiler
; Searching a LONG formlist of Ordered Forms (by formID); Binary Search Method adapted to Papyrus by tunaisafish from; http://en.wikipedia.org/wiki/Binary_search_algorithm#Deferred_detection_of_equalityInt Function GetFormIndexOrdered(FormList List, Form Member) Global{Gets the index of Member in an ordered List - by FormID.  Returns -1 if not found}Int tgt = Member.GetFormID()Int imin = 0Int imax = List.GetSize() - 1While (imax > imin)  Int imid = (imin + imax) / 2  If (tgt > List.GetAt(imid).GetFormID())   imin = imid + 1  Else   imax = imid  EndIfEndWhileIf ((imax == imin) && (tgt == List.GetAt(imin).GetFormID() ))  Return iminElse  Return -1EndIfEndFunction
User avatar
luis ortiz
 
Posts: 3355
Joined: Sun Oct 07, 2007 8:21 pm

Post » Thu Jun 21, 2012 6:17 am

When I add items to the CK, they are ordered by EditorID. How do I arrange them to be ordered with FormID? Don't tell me I'll need to manually rename them?

EDIT:
Daedric set alone consists of 106 items. Since there are Dragonplate, Dragonscale, Iron, Steel, Orcish, Dwarven, Steel Plate, Ebony, Leather, and all that, I expect the stuff to be at least 1100 items. It's gonna be crapload. Oh boy...
User avatar
Mashystar
 
Posts: 3460
Joined: Mon Jul 16, 2007 6:35 am

Post » Thu Jun 21, 2012 5:22 am

When I add items to the CK, they are ordered by EditorID. How do I arrange them to be ordered with FormID? Don't tell me I'll need to manually rename them?

When you drag a group of items into a formlist, they're sorted in the same order as they appear in the LHS pane.
So click the FormID column header before the move. (You might have to drag it wider so you can see it.)
User avatar
sophie
 
Posts: 3482
Joined: Fri Apr 20, 2007 7:31 pm

Post » Thu Jun 21, 2012 5:10 am

So I have to have the Object Window to list all armors that will get into the FormList before I drag them into the FormList?

Not a bad idea. I'll have to create all the Forms anyway. I'll get back to you in at least a week lol. Unlimited Form Works
User avatar
Philip Lyon
 
Posts: 3297
Joined: Tue Aug 14, 2007 6:08 am

Post » Wed Jun 20, 2012 4:34 pm

Wait, I just got an idea

What if I create multiple FormLists, each FormList for each category?

Like, say FormListA is for Daedric, FormListB is for Dragonplate, and so on.

This means that, if the item is at the extreme end of the List, rather than looking through literally hundreds of irrelevant entries, I will only at max go through 25 if else statement

Is HasForm fast enough to take advantage of this system? Or do I have to fall back to binary search as posted above? I shudder at thinking how would I sort them all. After all, not ALL items are considered
User avatar
Darren Chandler
 
Posts: 3361
Joined: Mon Jun 25, 2007 9:03 am

Post » Thu Jun 21, 2012 7:05 am

I'm getting the sense that you are putting off the mundane part of this mod for as long as possible lol. Not that I blame you.

Splitting to multi formlists won't change the bottleneck #1, and will just make #2 more complex. (#2 is already down to 10 iterations).
Run a dummy test on these, just to print a Notification() of the index found. You'll then have a real sense of if the mod will be too laggy or not.
User avatar
Ricky Meehan
 
Posts: 3364
Joined: Wed Jun 27, 2007 5:42 pm

Post » Wed Jun 20, 2012 7:37 pm

I'm getting the sense that you are putting off the mundane part of this mod for as long as possible lol. Not that I blame you.

Splitting to multi formlists won't change the bottleneck #1, and will just make #2 more complex. (#2 is already down to 10 iterations).
Run a dummy test on these, just to print a Notification() of the index found. You'll then have a real sense of if the mod will be too laggy or not.
No, not really. I've already done... wait...

Erm.... Archmage, Bandit, Beggar, Blades, Chef, Daedric, Dark Brotherhood, Dragonplate, Dragonscale, Draugr, Dwarven, Ebony,

If Daedric, Dragonplate, Dragonscale, Dwarven, Ebony each has 106 items, I've done at least 530 records. Not a bad haul, but it does numb my mind, yes (and my hands and arms)

Still have Elven, Falmer, Farm Clothes, Fine Clothes, Forsworn, Glass, Imperial, Iron, Jester, Mage Apprentice, Mage Journeyman, Merchant, Miner, Monk, Necromancer, Nightingale, Steel Plate, Orcish, Prisoner, Redguard, RobeDB, RobeMD, Sons of Talos, Stormcloaks, Steel, Studded, Thalmor, Thieves Guild, Warlock, Wedding, Wench, Wolf....



SHHIIIIEEETTTT.... still a loooong way to go.

Yeah, well I can make bottleneck#1 the first "if" check. What makes me reluctant to go with your (obviously superior) method is the nightmare of clicking "armor" and manually find which items I don't use and which items I DO use. Because seriously, they already have so many accessories. It's a real pain in the crap to pick which armors I use among those things. Also, I won't know whether the item in index 150 in FormList A will match the item in index 150 in FormList B. Either Talos guide me, or Talos screw with me

But I guess if I managed to pull creating all these armors off, I guess I might as well go with your method

On a side note, are clothings (such as Fine Clothes) count as Armor as far as object type is concerned?
User avatar
Stephanie Nieves
 
Posts: 3407
Joined: Mon Apr 02, 2007 10:52 pm

Post » Thu Jun 21, 2012 12:06 am

Tuna, sorry for the late reply. I've managed to duplicate literally around 1400~1500 forms. Talos works in mysterious ways.

And comes the dreaded problem: the function is way too slow. I don't know if it's my machine or the game, but since it took 5 goddang seconds to actually run the item switching function (marked with a message box), I find it to be a very BIG problem

But therein comes another problem: sorting by FormID produces very different results between the vanilla armors and my creations.

Specifically, mine is neatly ordered, since I duplicate them from top-down systematically

The vanilla are a jumbled mess, looks like the devs didn't create them systematically

It looks like I'll have to manually add them by hand. Again

Dear god. I just hope that your script works. It DOES work, doesn't it...?
User avatar
noa zarfati
 
Posts: 3410
Joined: Sun Apr 15, 2007 5:54 am


Return to V - Skyrim