Page 2 of 3
Universal Item Display Script (Weapons, Shields, Potions etc
![Post Post](http://www.gamesas.com/styles/proFormell/imageset/icon_post_target.gif)
Posted:
Thu Jun 21, 2012 1:40 pm
by Alexandra Louise Taylor
I haven't taken a look at the book case script, but what makes it so resource intensive? Maybe I can try re-writing it too.
Universal Item Display Script (Weapons, Shields, Potions etc
![Post Post](http://www.gamesas.com/styles/proFormell/imageset/icon_post_target.gif)
Posted:
Thu Jun 21, 2012 6:42 am
by Stefanny Cardona
the container, trigger and activator all have OnCellLoad events, particularly the container script which iterates through all possible books. harmless when you're talking about 1 or 2 bookcases (as is the case with all vanilla player homes), but multiply all of these event calls by the number of bookcases + number of mannequins + number of vanilla weapon racks all competing for the same event.
in the case with vanilla weapon racks and mannequins, these load events are timing critical, so when there are so many lines of code in the thread queue, the event block for the mannequins and weapon racks may not fire properly, hence the wandering problems and weapon floating/upside down etc (just my theory, but reducing the number of these types of furniture always solves the problem)
the bookcase script just looks like it could use some optimization. i have a feeling it can do away with the cell load event altogether. even having the book iteration triggered by a trigger box instead of a cell load would greatly improve the speed of the load call of the other furniture that needs it more (mannequins)
all of the cellLoad calls for the setnofavorallowed() are completely unnecessary IMO. those can be eliminated entirely. you can just query for player in the OnActivate block instead. as for the cellLoad for BlockActivation() that only needs to be called once afaik, so it can be nested inside a state so that it is called only once when it first loads in the mod, and then never called again
Universal Item Display Script (Weapons, Shields, Potions etc
![Post Post](http://www.gamesas.com/styles/proFormell/imageset/icon_post_target.gif)
Posted:
Thu Jun 21, 2012 1:17 pm
by Crystal Clarke
for other display types such as dagger case, shield rack, potion rack etc, leave a comment and i will detail which properties you need to fill, or if you can decipher the code in the AllowedItems function, it should tell you which properties will be used for that type. but in any case, DisplayType, and both Message properties must be filled for all types.
As a newbie modder, deciphering code is way beyond my level of understanding! If you could explain the properties required for shield racks and potion racks I would be eternally grateful
![:)](http://www.gamesas.com/images/smilie/smile.png)
I had no idea that using numerous vanilla display racks/cases/bookshelves could cause issues in game
![:confused:](http://www.gamesas.com/images/smilie/confused.gif)
A few of my mods contain large display areas and libraries so I will be working through and replacing all the vanilla racks using your tutorial.
And thanks for posting this tutorial - I'm sure plenty of people like myself who don't understand scripting will appreciate it!
Universal Item Display Script (Weapons, Shields, Potions etc
![Post Post](http://www.gamesas.com/styles/proFormell/imageset/icon_post_target.gif)
Posted:
Thu Jun 21, 2012 11:41 am
by stevie critchley
for potion racks, set your display type property to a value of 5
you will need to additionally fill the following keyword properties:
VendorItemPotion
VendorItemPoison
pressing auto-fill should fill those automatically (use the Auto-fill in the Edit Value section, NOT the Auto-Fill All button)
you will also need to create a Formlist with all variants of the WhitePhial since that item was never marked with any keywords by bethesda, and they will otherwise be denied when trying to place them on the display. follow the instructions i gave the user above for creating a formlist, but instead of the claws you will need to search "MS12" in the Potion section in the object window (MS12 is the name of the quest that handles the White Phial). after you create your formlist, fill the WhitePhial property with it.
the rest is the same as tutorial. there is a dummy potion marker you can use for the container model in the same folder as the dummy sword.
the shield rack is a bit more complicated. the way i have my shield rack set up in this script is a "coat of arms" style (where you can mount 2 swords behind the shield in a "X") for reference here is a photo how i set mine up in the render window:
http://i50.tinypic.com/2vwy13o.jpg
I'm using the default weapon plaque model as a Static, and the white trigger boxes as activators (they are in clutter/weaponrack).
you need to make sure you set up the trigger boxes and containers spaced apart enough so that when weapons and shield are mounted, they do not "invade" into the other trigger boxes (that's why i have mine shaped and angled strangely, mainly to accomodate larger shields) if a shield pokes into one of the weapon's triggers it will falsely return as that weapon trigger being "filled" (and no weapons will be allowed to mount in it), so give it enough space to avoid having a shield poke into it. the trigger boxes need to only be big enough to register a partial collision with the display item.
as for properties, the shield and weapons are handled separately. If you are making a shield-only display, you can disregard all the stuff about weapons and the wierd trigger box setup i described above
Shields - fill the following property in addition to the required ones, and set display type to 4:
ArmorShield
"Coat of Arms" weapons - fill the following and set type to 7:
WeapTypeSword
WeapTypeGreatsword
(note that only swords and greatswords will be allowed to mount)
Universal Item Display Script (Weapons, Shields, Potions etc
![Post Post](http://www.gamesas.com/styles/proFormell/imageset/icon_post_target.gif)
Posted:
Thu Jun 21, 2012 12:42 am
by Jessica Thomson
the container, trigger and activator all have OnCellLoad events, particularly the container script which iterates through all possible books. harmless when you're talking about 1 or 2 bookcases (as is the case with all vanilla player homes), but multiply all of these event calls by the number of bookcases + number of mannequins + number of vanilla weapon racks all competing for the same event.
The scripts all have a boolean guard to make sure they only go through that once, especially since the player homes don't reset. But I do see your point. I'm not sure that I can re-write the script to make it more efficient, though I could definitely make it shorter.
Universal Item Display Script (Weapons, Shields, Potions etc
![Post Post](http://www.gamesas.com/styles/proFormell/imageset/icon_post_target.gif)
Posted:
Thu Jun 21, 2012 4:23 am
by .X chantelle .x Smith
i was thinking of moving that load block to a function (called by an external trigger box) or RegisterForSingleUpdate(FloatProperty) in the load block and call the rest in the update event (enough time for mannequins to run their load scripts before bookshelves). the Register/Update will be only called in an "init" state then moved to a new state where it is never called again. the register time as a float property so that you can delay the update event to come in waves (if you really have a ton of them). when you fill the properties of each bookshelf you can use the float property as individually customizable "dynamite fuses" (in theory)
i havent gotten around to testing any of it yet though
if the dynamite fuse works, i will probably apply it to mannequins as well (so that all immediately visible mannys will get the instant OnLoad treatment, and the other secluded ones can get the fuse delay, so they dont all bum rush the load event at once
script-wise, the performance bottlenecking really only happens at the load event (texture ram strain aside)
Universal Item Display Script (Weapons, Shields, Potions etc
![Post Post](http://www.gamesas.com/styles/proFormell/imageset/icon_post_target.gif)
Posted:
Thu Jun 21, 2012 9:51 am
by Kristina Campbell
Re-wrote the container script using formlists and arrays, which halves the size. Haven't tested it though.
Spoiler Scriptname PlayerBookShelfContainerScript extends ObjectReference;;;;;;;;;;;;;;;;;; Properties ;;;;;;;;;;;;;;;;;;Message Property BookShelfFirstActivateMESSAGE Auto{Display message when the player activates a bookshelf for the first time. Only displays once.}Message Property BookShelfNoMoreRoomMESSAGE Auto{Displayed message for when the amount of books the player is placing excedes the shelf limit.}Message Property BookShelfNotABookMESSAGE Auto{Message displayed when the player places a non book form in the container.}Message Property BookShelfRoomLeftMESSAGE Auto{Notification that tells the player how much room is left on the shelf upon first activating it.}GlobalVariable Property BookShelfGlobal Auto{Global showing whether or not the player has ever activated a bookshelf}FormList Property MarkerKeywords Auto{Formlist containing all the bookmarker keywords}FormList Property TriggerKeywords Auto{Formlist containing all the booktrigger keywords}Form[] Property StoredBooks Auto{Array containing exactly which types of books are in the container}ObjectReference[] Property StoredRefs Auto{Array containing the references to the books on the book shelf}ObjectReference[] Property Markers Auto{Array containing the references to the book markers}ObjectReference[] Property Triggers Auto{Array containing the references to the book shelf triggers}Int Property MaximumBookAmount AutoInt Property CurrentBookAmount Auto;;;;;;;;;;;;;;;;; Functions ;;;;;;;;;;;;;;;;;Function AddBooks(Form BookBase, Int BookAmount) int i while (BookAmount) if !(StoredBooks[i]) StoredBooks[i] = BookBase BookAmount -= 1 endif i += 1 endwhileEndFunctionFunction RemoveBooks(Form BookBase, Int BookAmount) int i = MaximumBookAmount while (BookAmount) i -= 1 if (StoredBooks[i] == BookBase) StoredBooks[i] = None BookAmount -= 1 endif endwhileEndFunction;;;;;;;;;;;;;;;;;;;;; Events/States ;;;;;;;;;;;;;;;;;;;;;Auto State SettingUp Event OnCellAttach() RegisterForSingleUpdate(Utility.RandomFloat(3, 7)) ;this will hopefully make for less conflicts with other scripts starting up EndEvent Event OnUpdate() BlockActivation() StoredBooks = new Form[18] StoredRefs = new ObjectReference[18] Markers = new ObjectReference[18] Triggers = new ObjectReference[4] ObjectReference TempRef int i while (i < MarkerKeywords.GetSize()) Markers[i] = GetLinkedRef(MarkerKeywords.GetAt(i) as Keyword) if (Markers[i]) i += 1 MaximumBookAmount = i else i = MarkerKeywords.GetSize() endif endwhile i = 0 while (i < TriggerKeywords.GetSize()) Triggers[i] = GetLinkedRef(TriggerKeywords.GetAt(i) as Keyword) if (Triggers[i]) i += 1 else i = TriggerKeywords.GetSize() endif endwhile GoToState("Closed") EndEventEndStateState Closed Event OnActivate(ObjectReference akActionRef) BookShelfRoomLeftMESSAGE.Show((MaximumBookAmount - CurrentBookAmount)) if (BookShelfGlobal.GetValue() == 0) BookShelfFirstActivateMESSAGE.Show() BookShelfGlobal.SetValue(1) endif int i while (Triggers[i]) (Triggers[i] as PlayerBookShelfTriggerSCRIPT).GoToState("IgnoreBooks") i += 1 endwhile GoToState("Opened") Activate(akActionRef, True) EndEvent EndStateState Opened ;hope that this event works on containers, otherwise will need an update loop to check for menu mode Event OnClose(ObjectReference akActionRef) int i = MaximumBookAmount while (i > 0) i -= 1 if (StoredRefs[i]) StoredRefs[i].Disable() StoredRefs[i].Delete() StoredRefs[i] = None endif endwhile while (i < CurrentBookAmount) StoredRefs[i] = Markers[i].PlaceAtMe(StoredBooks[i]) i += 1 endwhile i = 0 while (Triggers[i]) (Triggers[i] as PlayerBookShelfTriggerSCRIPT).GoToState("WaitForBooks") i += 1 endwhile GoToState("Closed") EndEvent Event OnItemAdded(Form akBaseItem, int aiItemCount, ObjectReference akItemReference, ObjectReference akSourceContainer) if !(akBaseItem as Book) GoToState("IgnoreAll") RemoveItem(akBaseItem, aiItemCount, True, akSourceContainer) BookShelfNotABookMESSAGE.Show() GoToState("Opened") Return elseif (CurrentBookAmount == MaximumBookAmount) GoToState("IgnoreAll") RemoveItem(akBaseItem, aiItemCount, True, akSourceContainer) BookShelfNoMoreRoomMESSAGE.Show() GoToState("Opened") Return endif if (CurrentBookAmount + aiItemCount > MaximumBookAmount) GoToState("IgnoreAll") RemoveItem(akBaseItem, CurrentBookAmount + aiItemCount - MaximumBookAmount, True, akSourceContainer) aiItemCount = MaximumBookAmount - CurrentBookAmount GoToState("Opened") endif CurrentBookAmount += aiItemCount AddBooks(akBaseItem, aiItemCount) EndEvent Event OnItemRemoved(Form akBaseItem, int aiItemCount, ObjectReference akItemReference, ObjectReference akDestContainer) CurrentBookAmount -= aiItemCount RemoveBooks(akBaseItem, aiItemCount) EndEventEndStateState IgnoreAllEndState
Universal Item Display Script (Weapons, Shields, Potions etc
![Post Post](http://www.gamesas.com/styles/proFormell/imageset/icon_post_target.gif)
Posted:
Thu Jun 21, 2012 4:47 am
by Sammi Jones
looks good, i'll give it a go when i get a chance. really interested to see if the delayed-fuse register/update helps the threading bottleneck
Universal Item Display Script (Weapons, Shields, Potions etc
![Post Post](http://www.gamesas.com/styles/proFormell/imageset/icon_post_target.gif)
Posted:
Thu Jun 21, 2012 1:00 pm
by Alex Blacke
Hi guys
Another question for you please. I've been wondering if I can have multiple items mounted to the one activator. So for example let's say I have a single shelf and want to have mounting points for all 8 dragon claws on that one shelf. I can create 8 containers and triggers but the activator that they sit on (the shelf) will allow only one item to be stored. When you try to open the next container it just rejects the request as there is already an item stored. Or at least I think that's what is happening. I can get it to store two items by adjusting the script on the activator but both items store on the same container instead of using the next container on the shelf.
So is there a way of using one physical mesh as the activator but having multiple containers/triggers on it? Hope that makes sense...
Universal Item Display Script (Weapons, Shields, Potions etc
![Post Post](http://www.gamesas.com/styles/proFormell/imageset/icon_post_target.gif)
Posted:
Thu Jun 21, 2012 3:34 pm
by Elisabete Gaspar
you can but it would require rewriting the activator script. it would need to have a function that tracks which triggers are currently being filled, and which ones are empty, then open the container of the first available empty mount. this is actually closer to the bookshelf type functionality. it would probably be better to use a different script for something like this (adapted version of the bookshelf).
Universal Item Display Script (Weapons, Shields, Potions etc
![Post Post](http://www.gamesas.com/styles/proFormell/imageset/icon_post_target.gif)
Posted:
Thu Jun 21, 2012 1:48 am
by Sasha Brown
Now that I think about it, my bookshelf container script svcks. It's messy.
If I wanted to make it compatible with everyone, I should have kept the same properties and not introduced any new ones, so that people can just put the compiled script into their folders instead of needing to create an esp.
If I wanted to actually change it into something that I'm satisfied with, I should have re-wrote the whole system, including the scripts for the triggers. I never did like the way that the game just cloned your books and put them into the bookshelf. That was the reason for the problem with the Oghma Infinium.
Universal Item Display Script (Weapons, Shields, Potions etc
![Post Post](http://www.gamesas.com/styles/proFormell/imageset/icon_post_target.gif)
Posted:
Thu Jun 21, 2012 8:35 am
by Andrew Perry
i'm working on a new bookshelf script now, and what i am finding is another huge problem is that for every book on the shelf, you need 2 persistent refs (the marker and the placed book itself), so if your bookshelf holds 24 books, thats at least 48 persistent refs, multiply that by the number of bookshelves and... you get the idea.
i'm trying to instead figure out a way to make a "passive" bookshelf, where you place a book into a container, the reference gets deleted (removeitem) and the base object is sent to a level list on its own, then the dummy book spawns the base book from the level list. getting the book to populate the level list, and pre-linking level lists to their corresponding dummies on the shelf is not a problem, but not sure if they can be spawned in real time. this method would at least eliminate all persistent refs if it actually worked (which, combined with the dynamite fuse OnLoad, could essentialy allow you to have as many bookshelves as your physical ram would allow, you would not have load bottlenecks or persistent ref residue buildup)
Universal Item Display Script (Weapons, Shields, Potions etc
![Post Post](http://www.gamesas.com/styles/proFormell/imageset/icon_post_target.gif)
Posted:
Thu Jun 21, 2012 3:08 pm
by Nymph
I haven't worked much with dummy items, but I thought that they only spawned (or transformed into) entries from the leveled list when their parent cell is loaded. I suppose you could try with the dummy items initially disabled and then enable them afterwards to see if the leveled list items spawn.
Of course, this way you would need to have a unique leveled list for each bookshelf. Which I suppose isn't too bad of a problem if it works...
But will this work in player homes? I mean, player homes are usually in a no reset zone. If the cell doesn't get reset, then the dummy items don't respawn, and they wouldn't re-populate the bookshelf, right?
EDIT: Never mind, I forgot you can just call http://www.creationkit.com/Reset_-_ObjectReference on them.
Universal Item Display Script (Weapons, Shields, Potions etc
![Post Post](http://www.gamesas.com/styles/proFormell/imageset/icon_post_target.gif)
Posted:
Thu Jun 21, 2012 3:47 am
by Alberto Aguilera
the dummy/levelitem didnt work out too well. and calling reset on the dummy did not let it spawn the book from the updated level list
using an array + base object formlist (multiple unique dummy books) + Game.FindClosestReferenceOfTypeFromRef is the best i can do.
i'm using a modified Mannequin script ("armorslot" array for placed books) but it doesnt have any OnLoad event, and since i'm using the findClosestRef function, the dummy markers are only persistent for the length of the activation, then released after the OnUpdate is finished.
this script is less of a burden overall, but a little bit more "expensive" per interaction, which IMO is a much better tradeoff (i'd rather the stress be on the single interaction per bookshelf, rather than a bottleneck for all of them simultaneously). once i get it fully working i will post a new thread for it
this is intended to be used as a standalone script, not a replacer for vanilla (most of the vanilla playerhomes with bookshelves only have a couple in there and should be fine as is)
Universal Item Display Script (Weapons, Shields, Potions etc
![Post Post](http://www.gamesas.com/styles/proFormell/imageset/icon_post_target.gif)
Posted:
Thu Jun 21, 2012 6:03 am
by lydia nekongo
ok, ive finished the new bookshelf system
here are the main differences between this one and vanilla:
pros:
it uses only 2 scripts (one activator and one container)
no triggers needed
no load calls needed (thus can only be used in noReset zones)
half the number of persistent refs and properties (only the placed books remain persistent in ObjRef properties, no need for dummy marker persistence)
one container for the entire shelf
works independently of shelves (doesnt matter if you are using a 2 or 3 shelf book case, the container script will automatically figure out where to place the books)
very easy to optionally pre-populate the entire shelf with a separate container with pre-loaded books in it
easier to set up than the vanilla system
cons:
not backwards compatible with vanilla system at all (no chance of using this for vanilla homes, but is there isnt any need to anyway)
per-activation papyrus strain is slightly heavier than vanilla (since it has to crunch more code and function calls than the vanilla script activation), but IMO this is a better tradeoff for player homes with lots of clutter and multiple bookshelves/mannequins/weapon racks
i will start a new thread with a mini tutorial for any of you who are interested in trying it out
Universal Item Display Script (Weapons, Shields, Potions etc
![Post Post](http://www.gamesas.com/styles/proFormell/imageset/icon_post_target.gif)
Posted:
Thu Jun 21, 2012 6:22 am
by Eliza Potter
I had what I thought was an awesome idea to do away with any persistent refs, but it didn't work out. ![:(](http://www.gamesas.com/images/smilie/frown.gif)
I was going to have the trigger zone scripted with an http://www.creationkit.com/OnTriggerEnter_-_ObjectReference event and have it initially disabled. When I want to find the books on the shelf, I'd just enable the trigger zone and use the event to populate my placed books array.
Unfortunately, the event doesn't fire this way. Which makes no sense, because an initially disabled item in the trigger zone would fire the event when it gets enabled.Turns out it was just because I forgot to link the trigger zone to my container.
![:blush:](http://www.gamesas.com/images/smilie/blush.png)
Here's a video of my script in action: http://youtu.be/iS4u-dRs1io
And these are my two scripts (did not feel like going back to put in comments explaining how they work):
- The container script goes on the container, with the trigger zone as a linked ref, and two dummy books on either end of the shelf that are linked refs with keywords "BookShelfFirstBook" and "BookShelfLastBook":
Spoiler Scriptname fg109_Bookshelf_Container_Script extends ObjectReference Keyword Property BookShelfFirstBook AutoKeyword Property BookShelfLastBook AutoForm[] StoredBooksFloat ShelfLengthFloat CalcLengthInt CurrentBooksBool FullShelfEvent OnInit() BlockActivation() ShelfLength = GetLinkedRef(BookShelfFirstBook).GetDistance(GetLinkedRef(BookShelfLastBook)) + (GetLinkedRef(BookShelfFirstBook).GetHeight())EndEventAuto State Active Event OnActivate(ObjectReference akActionRef) GoToState("Inactive") CurrentBooks = (GetLinkedRef() as fg109_Bookshelf_Trigger_Script).MoveIntoContainer() StoredBooks = (GetLinkedRef() as fg109_Bookshelf_Trigger_Script).StoredBooks CalcLength = 0.0 FullShelf = False GoToState("Active") Activate(akActionRef, True) Utility.Wait(0.5) GoToState("Inactive") PlaceOnShelf() GoToState("Active") EndEvent Event OnItemAdded(Form akBaseItem, int aiItemCount, ObjectReference akItemReference, ObjectReference akSourceContainer) if !(akBaseItem as Book) RemoveItemSilent(akBaseItem, aiItemCount, akSourceContainer) Debug.MessageBox("The bookshelf only accepts books.") elseif (FullShelf) RemoveItemSilent(akBaseItem, aiItemCount, akSourceContainer) Debug.MessageBox("The bookshelf is full.") else int BookCount = CheckShelfSpace(akBaseItem, aiItemCount) if (BookCount < aiItemCount) FullShelf = True RemoveItemSilent(akBaseItem, aiItemCount - BookCount, akSourceContainer) endif AddBooks(akBaseItem, BookCount) endif EndEvent Event OnItemRemoved(Form akBaseItem, int aiItemCount, ObjectReference akItemReference, ObjectReference akDestContainer) if (akBaseItem as Book) RemoveBooks(akBaseItem, aiItemCount) FullShelf = False endif EndEventEndStateFunction RemoveItemSilent(Form akItem, Int aiCount, ObjectReference akContainer) GoToState("Inactive") RemoveItem(akItem, aiCount, True, akContainer) GoToState("Active")EndFunctionInt Function CheckShelfSpace(Form MyBook, Int Count) ObjectReference TempRef = PlaceAtMe(MyBook, 1, False, True) Float BookWidth = TempRef.GetHeight() + 0.5 TempRef.Delete() int i = 0 while (i < Count) && (CurrentBooks + i < 128) && (CalcLength <= ShelfLength) CalcLength += BookWidth i += 1 endwhile Return iEndFunctionFunction AddBooks(Form MyBook, Int Count) int i = 0 CurrentBooks += Count while (Count) while (StoredBooks[i]) i += 1 endwhile StoredBooks[i] = MyBook Count -= 1 endwhileEndFunctionFunction RemoveBooks(Form MyBook, Int Count) int i = 0 CurrentBooks -= Count while (Count) if (StoredBooks[i] == MyBook) StoredBooks[i] = None Count -= 1 endif i += 1 endwhileEndFunctionFunction PlaceOnShelf() ObjectReference[] PlacedBooks = new ObjectReference[128] ObjectReference First = GetLinkedRef(BookShelfFirstBook) ObjectReference Last = GetLinkedRef(BookShelfLastBook) Float AngX = First.GetAngleX() Float AngY = First.GetAngleY() Float AngZ = First.GetAngleZ() Float PosX = First.X Float PosY = First.Y Float PosZ = First.Z Float UnitVectorX = (Last.X - PosX) / ShelfLength Float UnitVectorY = (Last.Y - PosY) / ShelfLength Float UnitVectorZ = (Last.Z - PosZ) / ShelfLength Bool PlacedFirst = False Float OldHeight Float NewHeight Float Offset = 0.0 int i = 0 int j = 0 while (CurrentBooks) if (StoredBooks[i]) PlacedBooks[j] = DropObject(StoredBooks[i]) if (PlacedFirst) NewHeight = PlacedBooks[j].GetHeight() Offset += 0.5 * OldHeight + 0.5 * NewHeight + 0.5 OldHeight = NewHeight else PlacedFirst = True OldHeight = PlacedBooks[j].GetHeight() endif while !(PlacedBooks[j].Is3DLoaded()) ;do nothing endwhile PlacedBooks[j].SetMotionType(4) PlacedBooks[j].TranslateTo(PosX + UnitVectorX * Offset, PosY + UnitVectorY * Offset, PosZ + UnitVectorZ * Offset, AngX, AngY, AngZ, 100000.0) j += 1 CurrentBooks -= 1 endif i += 1 endwhile while (j) j -= 1 PlacedBooks[j].SetMotionType(1, True) endwhileEndFunction
- The trigger script goes on the trigger zone (which has collision layer L_Trigger), and it has the container as a linked ref:
Spoiler Scriptname fg109_Bookshelf_Trigger_Script extends ObjectReference Form[] Property StoredBooks AutoObjectReference MyContainerInt CurrentBooksBool LoopingEvent OnTriggerEnter(ObjectReference akTriggerRef) if (akTriggerRef.GetBaseObject() as Book) StoredBooks[CurrentBooks] = akTriggerRef.GetBaseObject() CurrentBooks += 1 MyContainer.AddItem(akTriggerRef) endif RegisterForSingleUpdate(0.2)EndEventEvent OnUpdate() Looping = False Disable()EndEventInt Function MoveIntoContainer() MyContainer = GetLinkedRef() StoredBooks = new Form[128] CurrentBooks = 0 Looping = True RegisterForSingleUpdate(0.2) Enable() while (Looping) Utility.Wait(0.1) endwhile Return CurrentBooksEndFunction
I also used the "ActivateLinkedChestDummyScript" for my click triggers.
The trigger zone and dummy books are initially disabled. The trigger zone covers the whole shelf area. Of course, there's a collision marker (with collision layer L_Unidentified) at the front of the bookshelf to stop books from falling out.
One good thing about this method is that you can pre-populate bookshelves in the CK just by putting the books inside the trigger zone.
Bad thing would be that the books are always randomly ordered since you can't control which of them fires the OnTriggerEnter event in which order. Would need to use SKSE to create a sorting function based on book names when placing the books.
Universal Item Display Script (Weapons, Shields, Potions etc
![Post Post](http://www.gamesas.com/styles/proFormell/imageset/icon_post_target.gif)
Posted:
Thu Jun 21, 2012 11:19 am
by Elle H
very interesting approach. very cool with the trigger disable/enable action you are using. this is awesome, i love sharing notes and comparing different approaches to the same problem and seeing varied ways to tackle a complex objective
this was the approach i came up with, although after some testing i found that the boolean guard for Bypass is not thread safe and is not reliable when the functions and while loops are cycling in multiple threads
Container:
Spoiler Scriptname DCVR_AH_BookshelfContainerScript extends ObjectReference ObjectReference[] Property PlacedBooks Auto HiddenForm[] Property Library Auto HiddenFormList Property BookMarkerList AutoMessage Property BookWarning AutoMessage Property CountWarning AutoMessage Property DupeWarning AutoInt Property MaxBooks AutoBool Property Blocked = False Auto HiddenBool Property Bypass = False Auto HiddenEvent OnInit()PlacedBooks = New ObjectReference[33]Library = New Form[33]EndEventEvent OnItemAdded(Form akBaseItem, int aiItemCount, ObjectReference akItemReference, ObjectReference akSourceContainer)Actor PlayerRef = Game.GetPlayer()If (akBaseItem as Book) If (IsDuplicate(akBaseItem)) || (aiItemCount > 1) If (akSourceContainer == PlayerRef) Bypass = True RemoveItem(akBaseItem, aiItemCount, True, akSourceContainer) DupeWarning.Show() Else Bypass = True RemoveItem(akBaseItem, aiItemCount) EndIf Else AddToLibrary(akBaseItem, aiItemCount, akSourceContainer) EndIfElse RemoveItem(akBaseItem, aiItemCount, True, akSourceContainer) BookWarning.Show()EndIfIf (akSourceContainer == PlayerRef) RegisterForSingleUpdate(0.1)EndIfEndEventEvent OnItemRemoved(Form akBaseItem, int aiItemCount, ObjectReference akItemReference, ObjectReference akDestContainer)If (akBaseItem as Book) && (!Bypass) RemoveFromLibrary(akBaseItem)EndIfBypass = FalseIf (akDestContainer == Game.GetPlayer()) RegisterForSingleUpdate(0.1)EndIfEndEventEvent OnUpdate()SyncBooks()EndEventBool Function IsDuplicate(Form akBaseItem)Int i = 0While (i < MaxBooks) If (Library[i] == akBaseItem) Return True EndIf i += 1EndWhileReturn FalseEndFunctionFunction AddToLibrary(Form akBaseItem, Int aiItemCount, ObjectReference akSourceContainer)Int i = 0Bool FoundEmptySlot = FalseWhile (i < MaxBooks) && (!FoundEmptySlot) If (!Library[i]) Library[i] = akBaseItem FoundEmptySlot = True EndIf i += 1EndWhileIf (!FoundEmptySlot) If (akSourceContainer == Game.GetPlayer()) Bypass = True CountWarning.Show() RemoveItem(akBaseItem, aiItemCount, True, akSourceContainer) Else Bypass = True RemoveItem(akBaseItem, aiItemCount) EndIfEndIfEndFunctionFunction RemoveFromLibrary(Form akBaseItem)Int i = 0Bool FoundMatchingSlot = FalseWhile (i < MaxBooks) && (!FoundMatchingSlot) If (Library[i] == akBaseItem) Library[i] = None FoundMatchingSlot = True EndIf i += 1EndWhileEndFunctionFunction SyncBooks()ObjectReference BookMarkerInt i = 0While (i < MaxBooks) Int n = 0 Bool FoundEmptySlot = False If (Library[i]) While (n < MaxBooks) && (!FoundEmptySlot) If (!PlacedBooks[n]) BookMarker = Game.FindClosestReferenceOfTypeFromRef(BookMarkerList.GetAt(n), Self, 800) PlacedBooks[n] = BookMarker.PlaceAtMe(Library[i]) RemoveItem(Library[i]) FoundEmptySlot = True EndIf n += 1 EndWhile EndIf i += 1EndWhileBlocked = FalseEndFunctionFunction ReturnBooks()Blocked = TrueInt i = 0While (i < MaxBooks) PlacedBooks[i].BlockActivation() Library[i] = None i += 1EndWhilei = 0While (i < MaxBooks) If (PlacedBooks[i]) If (PlacedBooks[i].Is3DLoaded()) AddItem(PlacedBooks[i].GetBaseObject()) PlacedBooks[i].Delete() EndIf PlacedBooks[i] = None EndIf i += 1EndWhileActivate(Game.GetPlayer())RegisterForSingleUpdate(0.1)EndFunction
Activator:
Spoiler Scriptname DCVR_AH_BookshelfActivatorScript extends ObjectReference ObjectReference Property BookContainer AutoEvent OnActivate(ObjectReference akActionRef)If (akActionRef == Game.GetPlayer()) If (!(BookContainer as DCVR_AH_BookshelfContainerScript).Blocked) (BookContainer as DCVR_AH_BookshelfContainerScript).ReturnBooks() EndIfEndIfEndEvent
Universal Item Display Script (Weapons, Shields, Potions etc
![Post Post](http://www.gamesas.com/styles/proFormell/imageset/icon_post_target.gif)
Posted:
Thu Jun 21, 2012 12:40 am
by Fluffer
on your script were you able to get around the issue with multiple books of the same form ID not removing itself properly from the form array?
the problem i found in testing was that if you added the same book twice to the same bookshelf, in the container it will show up as BookName (2), but in the form array it will be considered BookName as 2 separate values. then when you are in inventory menu, if you press the take-all button, since the 2 books are condensed into a single entry in the container's inventory, it will only fire OnItemRemoved one time with akBaseItem as Bookname and aiItemCount as 2. on my script, this removed only one entry from the form array, and left the other one intact, even though both books are physically gone from the container
i tried to combat this by disallowing duplicate books to be placed on the bookcase, so i added a Bypass boolean guard, but it doesnt fire properly due to the functions being called simultaneously in multiple threads, the boolean guard's real-time value cannot keep up with all the threads at once.
maybe if i pass aiItemCount into the remove from form array function, i can tell it to run the while loop for as many times as is in the value for aiItemCount (that way if BookName (2) is taken at once, it will purge BookName from the array twice)
Universal Item Display Script (Weapons, Shields, Potions etc
![Post Post](http://www.gamesas.com/styles/proFormell/imageset/icon_post_target.gif)
Posted:
Thu Jun 21, 2012 9:04 am
by xx_Jess_xx
Your idea is what I did. I passed in aiItemCount to my RemoveBooks() function in order to keep looking through the array until the passed in form has been removed "aiItemCount" times.
I used states instead of a boolean guard, which is better at keeping up with multiple threads. Of course, it's still not great. I found that when I added the placed books into the container, the OnItemAdded event was not keeping up with my while loop, and couldn't properly detect all the books that were added.
That's why I had to fill the array using the trigger zone script. Now that I look at it again, I also need to update my "CalcLength" variable in there too. I forgot to do that when I changed the script.
Universal Item Display Script (Weapons, Shields, Potions etc
![Post Post](http://www.gamesas.com/styles/proFormell/imageset/icon_post_target.gif)
Posted:
Thu Jun 21, 2012 12:50 pm
by Nathan Maughan
btw, the trigger method is brilliant, do you mind if i "borrow" it? heheh
Universal Item Display Script (Weapons, Shields, Potions etc
![Post Post](http://www.gamesas.com/styles/proFormell/imageset/icon_post_target.gif)
Posted:
Thu Jun 21, 2012 3:43 am
by Melanie Steinberg
No prob, use it all you like.
![:)](http://www.gamesas.com/images/smilie/smile.png)
Universal Item Display Script (Weapons, Shields, Potions etc
![Post Post](http://www.gamesas.com/styles/proFormell/imageset/icon_post_target.gif)
Posted:
Wed Jun 20, 2012 11:11 pm
by Stefanny Cardona
thanks. i think this definitely will make for better burden management for larger player houses
btw, you can get rid of permanent persistence for your book-end markers (linked refs are still flagged permanently persistent) by using Game.Findclosestwhatever, granted your book ends are created as 2 separate new dummy objects. that way you can find them by function call rather than linked ref or objref property
on my formlist i created a list of a ton of separate dummy book base objects, and placed them into the book case in the sequence they should be placed. then used game.findclosest to call them. this eliminates all persistent refs for markers. the formlist lets you populate multiple shelves in the same bookcase with a single container, but then again using individual shelf-specific book ends will probably also let you do the same (your shelf-measuring method is probably cleaner as it uses fewer objects in total).
Universal Item Display Script (Weapons, Shields, Potions etc
![Post Post](http://www.gamesas.com/styles/proFormell/imageset/icon_post_target.gif)
Posted:
Thu Jun 21, 2012 3:27 am
by Harry Leon
I'd actually tried not using the dummy books, but it didn't really work out.
I'd used the dummy book mesh for the container, and set it at one end of the shelf. I used it to determine the origin point for book placement and orientation. Then I got the shelf length simply by using GetWidth() on the trigger zone.
The problem with that is I'm using DropObject() and not duplicating books with PlaceAtMe(). When I dropped the books from the container, they popped up ~32 units above the container, right into the books on the shelf above. So whenever I tried to place books in one shelf, the books on the shelf above it would fly off everywhere.
EDIT:
BTW, are you sure that linked refs are permanently persistent?
Universal Item Display Script (Weapons, Shields, Potions etc
![Post Post](http://www.gamesas.com/styles/proFormell/imageset/icon_post_target.gif)
Posted:
Thu Jun 21, 2012 12:24 am
by Marquis T
one advantage i thought of with using the dummy object formlist + your trigger method is that you can auto-populate the shelves randomly using the dummies and leveleditems. as long as the dummies in the formlist are base objects, they dont get marked as persistent. they can serve 2 purposes (one as placement marker and one as leveled item placeholder)
i had similar problems with dropobject which is why i switched to placeatme/delete + form alias/clear (really hoping persistence issues are not going to mess everything up when 1.6 goes live otherwise this will cause massive problems, not only scripts like this but the vanilla scripts as well). i have my invisible container embedded into the center of the book case with the 3d model of the book case being the activator itself. originally i had the 3d model for the container just be the book case 3d, but as the script evolved, after placing the books onto the shelves it would read "Search Bookcase - Empty" even though there are visible books sitting on the shelves which might be a bit confusing for the player (the containers contents are never filled while books are placed outside of it, so that there is only ever 1 instance of the book in existence)
edit - 100% sure linked refs are perma-marked. you can tell by looking at persistent location in the cell view window it will show as (all) - which means permanetly persistent. it also shows flagged persistent in tesvsnip as well as show up in the persistence-only GRUP, and will cause an edit on vanilla records if marked as a linked ref (since you are changing that record's persistence permanently and moving the REFR into the persistence-only GRUP of that cell)
basically any time you are given the opportunity to select something in the render window with that red crosshair thing, it will flag that ref as perma-persistent and move it into the perma-GRUP
another fun tidbit is that if a REFR is sent to the perma-GRUP it stays there forever, unless you physically delete it in the CK. even if you un-link the ref or remove it from an objref property, it saves in the esp as perma-persist forever
Universal Item Display Script (Weapons, Shields, Potions etc
![Post Post](http://www.gamesas.com/styles/proFormell/imageset/icon_post_target.gif)
Posted:
Thu Jun 21, 2012 2:35 am
by Fluffer
Well... that totally svcks then. It's unbelievable, considering how many linked refs are used in the vanilla game.
I don't really want to use the FindClosestRef functions because they're not very accurate, and no doubt they're resource intensive. I suppose a complicated workaround would be to use GetFormFromFile from 1.6. Don't really want to do that though.