Jump to content

Differentiating non-unique items in a script


WarMachineDD7

Recommended Posts

I was making a quest to find all the skill books in the game (don't steal this mod idea, it's mine!) and the issue I'm running into is that I don't know how to mark that you've picked up a particular skill book of the same type versus any other (for example, how can I track that you picked up the Big Book of Science in Nipton vs picking up the Big Book of Science in Camp Forlorn Hope).

 

Ideally the mod will show waypoints to each skill book all at once and will update the objectives as you pick them up (I'll split everything up into multiple quests, one for Science skill books, one for Unarmed skill books, etc, but right now I just wanna get one working).

 

I got NVSE, JIP LN, and NX installed, so I can use any of their functions. Is there some easy way to differentiate these non-unique items that I'm overlooking? Obviously, the less I modify in the main game, the better (for compatibility's sake).

 

Here are a few ideas I thought of:

  1. Make all the skill books persistent references, and then add an object script to each skill book type with an OnAdd player block where they'll check if any skill book is missing from their respective cells (for example, when I grab the Big Book of Science in Nipton, the script will check Nipton, Camp Forlorn Hope, and any other place where a Big Book of Science is, to see if the item is missing and update the quest).
  2. Instead of doing all that, simply have the OnAdd block check the player location. If player is in Nipton when he picks up a Big Book of Science, then set the Nipton book objective as completed (easiest implementation of them all, I think).
  3. Another way to do it, though it seems like way more work, is to replace the skill books with copies of them with unique objectIDs and then replace the original skill books in each cell with my unique copies. Since every single skill book is unique, I can make them have their own object scripts that update their respective objective on pick up. I can even remove the item from the player and replace it with a regular skill book as well.
  4. Make all the skill books persistent references, and constantly check if the player is looking at a particular skill book when he presses E. I already checked this one and it works fine, but I'm not a fan of a script checking so often for nothing.

I'm leaning towards (2) right now since it's easy to implement and it's not a resource hog.

 

The most promising idea that didn't pan out was this one. Just like in (1), make persistent references and an object script for each skill book type with an "OnAdd player" block, but then check the player's inventory for the skill book when it gets picked up and check if it's one of the persistent references:

scn SkillBooksQuest_ScienceBook_SCRIPT

ref currentBook

Begin OnAdd player

	;Iterate through all the science skill books in the player inventory
	let currentBook := Player.GetFirstRefForItem BookSkillScience ;First book found
	while currentBook != 0 

		;"ScienceBook1" is the persistence reference I created for the science skill book in Nipton.
		if currentBook.GetIsReference ScienceBook1 == 1
			MessageBoxEx "You got the Science skill book from Nipton"
		endif

		let currentBook := Player.GetNextRefForItem BookSkillScience ;Go to next book

	loop

End

The currentBook variable will point to the skill book correctly (I checked earlier by making it output the name of the reference it got and it said "Big Book of Science"), but it doesn't get recognized as the same reference as ScienceBook1 (this was expected since the GetNextRefForItem function description says that it creates a temporary reference, but still disappointing). Is there another way to compare these references that will let me recognize it as the persistent reference?

Edited by WarMachineDD7
Link to comment
Share on other sites

You could preserve compatibility by running a quest script whenever a relevant cell is entered, and checking the item count until the cell is exited (and you should stop the quest at that point, so it runs only in that cell). That would require an OnCellEnter event handler. Otherwise, you could use dummy items with GameMode script blocks that do the same thing. The quest script and event handler method would be the most compatible, followed by the dummy item, but an OnAdd block directly on the book would be the most secure. The thing with the OnAdd block is it assumes the book wasn't moved by another mod unless you do the Persistent Reference method. Then again, you can't use the books as quest markers unless they're persistent.

 

So, you could make them persistent, then use an OnCellEnter event handler to check is one of the books there any time you enter a cell, and start the quest script for that book only if it is. This will probably give you the most compatibility and security at once. It's a good idea to remove the OnCellEnter event handler once the books are all found. (And remember that it has to be set every time you start the game as it doesn't save to your save files).

 

Edit: Or you could use an OnAdd event handler and just take the chance that the books will never be in a container. I don't think they ever are in a container in the vanilla game. But I think I'd find that too risky.

Edited by EPDGaffney
Link to comment
Share on other sites

Or you could use an OnAdd event handler and just take the chance that the books will never be in a container. I don't think they ever are in a container in the vanilla game. But I think I'd find that too risky.

 

I think the only one to worry about is the skill book in the Mojave Outpost because it can either be stolen or bought from the vendor there. Every single other skill book is outside a container. Thanks a lot for letting me know about the OnCellEnter, that seems super useful and I just might use it for that particular skill book I mentioned.

 

Cursory glance at mods that affect skill books shows I'm pretty safe regardless of what I do (none seem to move them out of place unless it is to put them all on the same place, and I don't care to be compatible with those mods). And the cool thing about option (2) is that I can make the main quest do an initial check for all skill books that are not on their respective cells and mark them as completed from the get-go, making the quest retroactive.

Edited by WarMachineDD7
Link to comment
Share on other sites

If you make them persistent references, if you want to put in the work, you can make the mod compatible with pretty much anything. The references will only be destroyed if the books are placed in an inventory before the game is loaded. I had another idea for even better compatibility which I didn't mention, using GetFormFromMod, but it didn't work because the non-persistent references wouldn't be loaded into memory and no functions would operate on them. As you're going ahead and marking them persistent from the outset, you can do things like check their containers, check if they're valid forms, check what cell they're in if they aren't in a container, stuff like that.

 

If they're not valid forms, the references have been destroyed and I suppose the best thing you can do is check all the containers in the vanilla cell that should have the relevat book (looping through an array made by GetRefsInCell), and if it isn't in any of them, you can mark that book found. Remember, you can use AddObjectiveTarget and other quest functions to do almost anything you want.

 

Edit: Persistent references will be destroyed like other references once placed in a container. I didn't mean to imply otherwise. But it won't matter for what you're doing unless it occurred before your mod was loaded.

Edited by EPDGaffney
Link to comment
Share on other sites

Persistent references will be destroyed like other references once placed in a container. I didn't mean to imply otherwise. But it won't matter for what you're doing unless it occurred before your mod was loaded.

 

Well, here's a surprise, persistent references don't disappear at all. Ever. Not even if they've been taken and consumed.

 

Without having ever loaded this mod, I made a character and went to Nipton Hall 3rd floor where the a Science Skill Book is, then I made a save in front of the skill book (let's call it Save A). I then took and consumed the skill book and made another save (Save B). Afterwards, I loaded my mod where I set the skill book to be a persistent reference, and when I start the game, both Save A and B say that the skill book is there and working (even though nothing is there in Save B). I can call and use those references just fine in either save file (for example, I set the quest marker there and it points to where the book would be in B). Functions like IsFormValid, GetDisabled, GetNumRefsInCell, IsReference, and GetRefCount, all return that the reference is there, enabled, and one of them. I even tried

GetRefsInCell NiptonTownHallInteriorFloor3 25 0 0

And after reading it back in a foreach loop, I get a reference there to the skill book on save B just fine.

 

I can't note if the skill book has been taken at all if I set it as persistent.

Edited by WarMachineDD7
Link to comment
Share on other sites

That's weird. I may need to run some tests when I get the chance, but what you're describing isn't normal functionality. I remember when I first learnt that persistent references are destroyed once put into the inventory, as it was a major problem in my quest mod. It knew the player had the item, but only until I think it was the next time it loaded an exterior cell. Then the reference was treated as null. Looking into it, I found that was standard behaviour. I could be missing some nuance but that's the way I remember it.

 

I'll see what I can come up with later.

Link to comment
Share on other sites

All right, so, it seems they're not technically destroyed, but I think this has to do with being mod-added. Even MarkForDelete doesn't actually delete mod-added forms, we found out. The object itself is vanilla, yes, but the fact that it's persistent is the result of a mod. I'm not sure on this, but it's not really relevant anyway.

 

So, if you consume the item or otherwise remove it from the world, its location ceases to update until you leave the interior cell, at which point it knows you 'have' it. But if you check its location via console or script, it will be the last location at which it existed, even in an inventory, before it was consumed.

 

I don't know that you can absolutely count on this, because I did have my persistent reference stop being recognised by the AI, at which point some research led me to veterans claiming the persistent reference does get destroyed at some point. Perhaps they were wrong about that but the game still doesn't completely understand what happens to the object once consumed.

 

If we can count on the game not 'losing' the object before it's consumed, then you can just check whether or not the player has it. Otherwise, you can just use an event handler with an OnActivate or OnAdd block. Both of these require the item not to be in a container when the player finds it, but that doesn't seem to be a problem for the scope of your mod, as we've covered. Test with the Mojave Outpost one, but that shouldn't be too hard to work around if needed.

 

Also, don't expect the quest marker to go away until you script it to do so. I'm almost certain that's normal behaviour, though I don't ever use quest markers myself, so don't quote me.

Link to comment
Share on other sites

The quest marker follows the object around except when it's in the inventory, but will continue to update if you drop it. This is ok by me since I only intend to check when you first pick it up. The issue I have right now is that the permanent reference tells my script that the object is still there in the cell even when it isn't, and I can't think of another way to check if that's true. All the functions I can think of tell me the reference is valid and that it points to a skill book as if it was there for the player to pick up (when it clearly isn't like in the case of Save B).

Edited by WarMachineDD7
Link to comment
Share on other sites

Well, GetContainer should return the player. When I used the book, it returned 0, so it knew it was no longer in a container. I should have tested before I did that but forgot to. I'm pretty sure that would work, though.

 

And there are the event handlers as well.

 

Failing that, player.GetItemCount <Book Name>, if you monitor the count at the right times, should tell you what you need to know.

Link to comment
Share on other sites

  • Recently Browsing   0 members

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