Tracking which books have been read.

Post » Tue Jun 19, 2012 1:23 pm

I'm currently working on a script that attaches to certain books and am using the OnRead() event to do some stuff once they have been read.

I'm also trying to do this stuff only once after the book is read. I have tried using states to block it from occurring more than one time, but that's only blocking the event on the instance of the item that I have just read. It does not block it from occurring on other instances of the book in the game world.

The other thing I've been trying which I can't get to work is using a form list to track which books were read with something like this:

ScriptName BookScript extends ObjectReferenceFormList Property BookTracking autoAuto State NotRead	 Event OnRead()		  GoToState("Read")		  if !BookTracking.HasForm(self.GetBaseObject())			   BookTracking.AddForm(self.GetBaseObject())			   ;Do stuff here.		  endif	 EndEventEndStateState Read	 ;Do nothing. Book was read.EndState

I'm not sure if that's a legitimate use of a FormList, but regardless, it's not blocking other instances of the book from doing stuff upon reading.
User avatar
Colton Idonthavealastna
 
Posts: 3337
Joined: Sun Sep 30, 2007 2:13 am

Post » Tue Jun 19, 2012 4:26 am

Anyone have any ideas?
User avatar
WTW
 
Posts: 3313
Joined: Wed May 30, 2007 7:48 pm

Post » Tue Jun 19, 2012 5:45 pm

I have been looking at same thing.

http://www.creationkit.com/Book_Script - Book Script is either non-existent or documentation is required; for a start

I had to use a similar workaround to you ... I wanted to advance a questline by reading a book. If the book was re-read, I resorted to checking the stage of the quest and only advancing if not at applicable stage.

I can find no "HasBeenRead" property.
User avatar
My blood
 
Posts: 3455
Joined: Fri Jun 16, 2006 8:09 am

Post » Tue Jun 19, 2012 3:32 pm

Use self.GetRef().GetBaseObject()

Edit: Err.... sorry was assuming you were attaching the script via an alias when I wrote the above..
How are you attaching it?
User avatar
Steve Smith
 
Posts: 3540
Joined: Sat Jun 30, 2007 10:47 am

Post » Tue Jun 19, 2012 8:38 am

I'm attaching the above quoted script to a book in the editor, and I'm attaching an empty Form List object to the "BookTracking" variable in the editor.

Right now, the error I'm encountering when I test the script in game after reading the same book multiple times is this:
[02/24/2012 - 11:33:00PM] error: Unable to call GetBaseObject - no native object bound to the script object, or object is of incorrect typestack:[Item 2 in container  (00000014)].BookScript.GetBaseObject() - "" Line ?[Item 2 in container  (00000014)].BookScript.OnRead() - "BookScript.psc" Line 6
User avatar
alicia hillier
 
Posts: 3387
Joined: Tue Feb 06, 2007 2:57 am

Post » Tue Jun 19, 2012 7:13 pm

Also, if I change the BookTracking.HasForm(self.GetBaseObject()) to BookTracking.HasForm(self), the errors disappear, but still is not functioning as desired.
User avatar
Tanika O'Connell
 
Posts: 3412
Joined: Fri Jan 26, 2007 1:34 am

Post » Tue Jun 19, 2012 2:47 pm

I have been looking at same thing.

http://www.creationkit.com/Book_Script - Book Script is either non-existent or documentation is required; for a start

I had to use a similar workaround to you ... I wanted to advance a questline by reading a book. If the book was re-read, I resorted to checking the stage of the quest and only advancing if not at applicable stage.

I can find no "HasBeenRead" property.

Book Script is empty, there is nothing to document. It extend Form, so the documentation is there : http://www.creationkit.com/Form_Script

And for your problem, you have IsStageDone() : http://www.creationkit.com/IsStageDone_-_Quest
User avatar
Matt Gammond
 
Posts: 3410
Joined: Mon Jul 02, 2007 2:38 pm

Post » Tue Jun 19, 2012 7:04 am

I'd recommend looking at the scripts in my mod "Unread Books Glow". http://steamcommunity.com/sharedfiles/filedetails/?id=9846

Attaching scripts directly to book forms was my first approach, but it has problems:
1. The book text is now in English, even on foreign language versions of Skyrim. This is a big problem if it's a pre-existing book in the game, less of a problem if it's a new book you've written - and thus isn't translated anyway.
2. Books that already have scripts attached to them (Explorer's Guide to Skyrim, etc) bug out for unexplained reasons.
2. Getting that script OFF of the book, for example if you've updated your mod, is a big pain. Look in the wiki under "Save File Notes" and "Persistence" for all the gory details.

What I am doing now is creating a Quest, and having Quest Aliases attached to various books objects in the world. The quest gets restarted, and the quest aliases get reset, every time the player moves into another cell or location, via another quest and a quest aliases pointing to the player.

It's complicated and works kind of poorly, based on how frequently I have been putting out fixes. Still, you might learn from my mistakes.
User avatar
Danny Blight
 
Posts: 3400
Joined: Wed Jun 27, 2007 11:30 am

Post » Tue Jun 19, 2012 4:55 am

Also, I will, no kidding, give anyone $100 if you can figure out a better way to determine which books have been read.

I use the approach described here, putting book forms in a form list in the OnRead() handler, and it seems to work fine. But my mod doesn't know about books read before the mod was installed, which makes it much less useful than it could be.
User avatar
TASTY TRACY
 
Posts: 3282
Joined: Thu Jun 22, 2006 7:11 pm

Post » Tue Jun 19, 2012 2:33 pm

I took at look at the scripts in your mod and see you using an empty formlist to check for read status, though it's slightly different because you're using a ReferenceAlias script instead of an ObjectReference script, which is handled in your script just fine.

I created a script from scratch, attached it to a single book in the editor, spawn the book in my inventory with a console command, and am getting errors when I read it.

Here's the entire script:
Scriptname _TestScript extends ObjectReference  FormList Property BookTracking AutoEvent OnRead()  If !BookTracking.HasForm(Self.GetBaseObject())	BookTracking.AddForm(Self.GetBaseObject())  EndIfEndEvent

The BookTracking property is attached to my empty FormList I created in the editor I named BookTracking.

This is the error I keep getting which is related to calling the GetBaseObject() function.

[02/25/2012 - 01:55:18PM] error: Unable to call GetBaseObject - no native object bound to the script object, or object is of incorrect typestack:[Item 2 in container  (00000014)]._TestScript.GetBaseObject() - "" Line ?[Item 2 in container  (00000014)]._TestScript.OnRead() - "_TestScript.psc" Line 6

My script is attached to the book that I'm reading, so I have an object of ObjectRef type as the object I'm calling GetBaseObject() on, and it definitely exists, so I'm not really sure why I'm getting the error.
User avatar
ladyflames
 
Posts: 3355
Joined: Sat Nov 25, 2006 9:45 am

Post » Tue Jun 19, 2012 3:48 pm

Small breakthrough:

I tried activating the object while outside of my inventory and the script works fine (dropping the book on the ground). I'm running into issues when I try to read the book from my inventory.

Is there any reason that the GetBaseObject() function wouldn't work correctly on an item in my bags?
User avatar
Javaun Thompson
 
Posts: 3397
Joined: Fri Sep 21, 2007 10:28 am

Post » Tue Jun 19, 2012 10:08 am

It's maddening, isn't it? They should burn all books :wink:

Yeah, I use IsStageDone() to check whether I have read a particular book and so whether my Quest needs updating. But what was going to be quite complex is not, because I can't actually check that a book has been read (I am only checking the quest and assuming - and I am right, because I made the quest! - that something else has advanced which must be the result of reading the book). I half started to put together a quest that has a stage for every book in game (hard coded), then I thought ... hmmm, new books ... and abandonned that.

Somewhere on this forum (early in its life) there is a thread about being able to marry the lass who works at the shop in Riverwood. As far as I know, the guy never solved why it is impossible to make her marriageable ...

It seems there are a few things in game that do not behave the way we expect, with no indication of why ..

If one of you crack "HasBeenRead" please let me know ... will make my life easier :smile:



Another thing on Books:
There is a book called, "MQ202GissurNote". It includes some text-replacement to add player Race, six and name. There is no script attached to it. How does the note do the replacement!? I figured this out (well, kinda). The book is put in an NPC inventory using Reference Alias of the quest the book is attached to. The Reference Alias for the book has "Uses Stored Text" option ticked ... According to the wiki: http://www.creationkit.com/Quest_Alias_Tab#Checkboxes: Not a whole lot of info, but it's a start ...


AND another thing: "QABooksContainer" holds a copy of (supposedly) every book in game. Why? What is that meant to do? If a new book is added to the game (modded) what happens when that book is not (also?) in this container? (Nothing, apparently, as I have a functioning note that has never seen that container)


... Books ... burn 'em :wink:
User avatar
Ludivine Poussineau
 
Posts: 3353
Joined: Fri Mar 30, 2007 2:49 pm

Post » Tue Jun 19, 2012 10:30 am

Small breakthrough:

I tried activating the object while outside of my inventory and the script works fine (dropping the book on the ground). I'm running into issues when I try to read the book from my inventory.

Is there any reason that the GetBaseObject() function wouldn't work correctly on an item in my bags?

It doesn't seem an issue just with books. Using GetBaseObject in the OnEquipped event of a sword displays the same error.
User avatar
Scared humanity
 
Posts: 3470
Joined: Tue Oct 16, 2007 3:41 am

Post » Tue Jun 19, 2012 5:47 am

It's gotta be related to the container the item is stored in. What is the exact error message you're getting?
User avatar
Sarah Unwin
 
Posts: 3413
Joined: Tue Aug 01, 2006 10:31 pm

Post » Tue Jun 19, 2012 1:50 pm

This

[02/26/2012 - 03:08:34AM]  OnEquipped Event[02/26/2012 - 03:08:34AM]  Self = [ShanaTestBookScript ][02/26/2012 - 03:08:34AM] error: Unable to call GetBaseObject - no native object bound to the script object, or object is of incorrect typestack:[Item 2 in container  (00000014)].ShanaTestBookScript.GetBaseObject() - "" Line ?[Item 2 in container  (00000014)].ShanaTestBookScript.OnEquipped() - "ShanaTestBookScript.psc" Line ?

Second line is from a Debug.Trace("Self = " + Self ). It is curious that the name of script appears there. http://www.creationkit.com/Papyrus_Runtime_Errors#.22Unable_to_call_X_-_no_native_object_bound_to_the_script_object.2C_or_object_is_of_incorrect_type.22

I was reading persistence stuff in the wiki to see if it could be due to the item being unloaded ( even if it doesn't make any sense in my head ) but I'm too sleepy to get any sense of it right now...

EDIT: The script itself

Spoiler
Scriptname ShanaTestBookScript extends ObjectReference {When };==============================================FormList Property BookTracking AutoEvent OnEquipped(Actor akActor)  Debug.Trace(" OnEquipped Event")  Debug.Trace(" Self = " + Self)  Debug.Trace("  Self.GetBaseObject() = " + Self.GetBaseObject() )  If !BookTracking.HasForm(Self.GetBaseObject())    BookTracking.AddForm(Self.GetBaseObject())  EndIfendEvent
User avatar
courtnay
 
Posts: 3412
Joined: Sun Nov 05, 2006 8:49 pm

Post » Tue Jun 19, 2012 4:53 am

Looks to me like the GetBaseObject() function doesn't like the fact that your object reference is in the player's inventory too.

That's the same behavior I'm running into with my script (It's only working when I drop the book on the ground and refusing to work when I read the book from my inventory).

Is there anything I'm just missing entirely that would not allow that function to be called on something in my player's inventory?
User avatar
Jay Baby
 
Posts: 3369
Joined: Sat Sep 15, 2007 12:43 pm

Post » Tue Jun 19, 2012 4:53 pm

The only thought I have, is related to the one mentioned by Shana:

Does placing an Item in the PC's inventory (maybe any Actor's Inventory?) mean it is Unloaded? And that it is only loaded while outside of that inventory (on the ground etc)??

Try scripting a messagebox - or whatever - in the OnUnload() Event for the Book ... does it fire when you place in inventory?
User avatar
Jessica Nash
 
Posts: 3424
Joined: Tue Dec 19, 2006 10:18 pm

Post » Tue Jun 19, 2012 12:03 pm

Added this to the script:

Event OnUnload()  Debug.Trace("Book is unloaded")EndEvent

That didn't get triggered. Tried it with book on the ground and in my inventory.
User avatar
chirsty aggas
 
Posts: 3396
Joined: Wed Oct 04, 2006 9:23 am

Post » Tue Jun 19, 2012 1:03 pm

This can be because your script doesn't know the type of object, because it's not rendered in the game. You can try casting the type of the object, you know it's a book because your script is attached to a book. When an item is in the inventory, it's a form, when it's in the world, it's an objectreference. Form doesn't have the getbaseobject() function, only ObjectReferences have this function.
User avatar
Hairul Hafis
 
Posts: 3516
Joined: Mon Oct 29, 2007 12:22 am

Post » Tue Jun 19, 2012 7:09 pm

Dunno if it's related, but there's a note on the http://www.creationkit.com/OnItemAdded_-_ObjectReference page.

Once an object has been added to a container it is no longer valid to call native functions on it. Native functions on aiItemReference will most likely fail to run with an error.

I've been calling native functions on a script that's attached to an inventory object though with no problems. That's used with the OnContainerChanged() event to trigger the code to run when it's added to the player. Maybe only some native functions will fail.
User avatar
Joe Bonney
 
Posts: 3466
Joined: Tue Jul 17, 2007 12:00 pm

Post » Tue Jun 19, 2012 4:33 pm

:(

I was hoping that would be it ...

EDIT: Planteur's answer looks promising ... kind of what I was thinking with UnLoaded ... Is that a fix?!



(If it is not) - Question: Have you tried something a bit mad ... Hide an empty (non-respawning) chest somewhere in the world ... In a book's OnRead() event, check whether the chest's inventory contains the same book ... If not, add a copy of the book to the chest ...

(stupid, I know ... and you will need an OnRead() for every single book, which is a PITA ... but does that work?)
User avatar
His Bella
 
Posts: 3428
Joined: Wed Apr 25, 2007 5:57 am

Post » Tue Jun 19, 2012 9:19 am

Hum. When the object is in the container, you don't have to get the baseobject of the object, it's already the baseobject in the container. Edit : but not sure.
User avatar
xemmybx
 
Posts: 3372
Joined: Thu Jun 22, 2006 2:01 pm

Post » Tue Jun 19, 2012 8:23 am

I'm trying to figure out why OnRead is running in the first place now lol.
The objectref that it was attached to is hiding somewhere.

With duggelz glowing books mod, he uses quest Aliases to attach the script, so that will make the ref persistent and avoid these problems (?).
User avatar
Solina971
 
Posts: 3421
Joined: Thu Mar 29, 2007 6:40 am

Post » Tue Jun 19, 2012 11:05 am

I'm trying to figure out why OnRead is running in the first place now lol.
The objectref that it was attached to is hiding somewhere.

With duggelz glowing books mod, he uses quest Aliases to attach the script, so that will make the ref persistent and avoid these problems (?).
"Event received when this object (which is a book) has been read. In other words, it is sent when the book UI opens a book."

Books are weird (a bit like Activators, I think ... sorta) - They don't behave like other, more normal, items.

(they have the learn spell/skill thing attached, which other items do not ... which means they must be handled differently by the game when "activated" ... I think)

(I agree that QuestAlias are a great cheat ... I use it to advance my quest when a particular series of books are read ... but that's a very big PITA if you are going to track every book in the game ...)

Really, the player is not really ever using a book. It is either stored somewhere (in world or in a container or inventory) or it is being read. There is no "playerIsReading()" and no "bookIsEquiped" like there are for other items and their actions ... All of this is something to do with that ...
User avatar
Joe Alvarado
 
Posts: 3467
Joined: Sat Nov 24, 2007 11:13 pm

Post » Tue Jun 19, 2012 4:39 pm

Thanks for the help, I think I have a working solution now.


Scriptname _TestScript extends ObjectReference  FormList Property BookTracking AutoBook Property Book1 AutoEvent OnRead()Debug.Trace(Self)  If !BookTracking.HasForm(Book1)	BookTracking.AddForm(Book1)  EndIfEndEvent

This seems to work out if I point the Book1 variable to the book I'm attaching the script to in the editor.

By setting a variable to whatever the book is that I'm checking for, I don't run into the issues of trying to grab the base object from the inventory. It's a little bit more work setting things up in the editor, but so far, this seems to be a fine solution for what I'm after, thanks for the input that lead me to the solution!
User avatar
Lucky Boy
 
Posts: 3378
Joined: Wed Jun 06, 2007 6:26 pm

Next

Return to V - Skyrim