Jump to content

[LE] [Script] Make an item (in this case, a book) delete itself when activated


Merrymac

Recommended Posts

I want to turn all existing spell tomes into bundles of scrolls, so that when the player "reads" them, an amount of different scrolls are received instead of learning a spell or opening the book.

 

Here's a script I have attached to a tome (which is changed to being a regular book), which does so successfully:

Scriptname _SB_ScrollBundleLarge extends ObjectReference

LeveledItem Property RandomScroll auto

Event OnRead()
  Game.DisablePlayerControls(False, False, False, False, False, True)

  Game.GetPlayer().AddItem(RandomScroll, 6)

  Game.EnablePlayerControls(False, False, False, False, False, True)
endEvent

However, I also need to remove the book itself (which the script is attached to), and this has to account for both the book being in the player's inventory, and the book being activated as an object in the world, outside the inventory menu.

 

Adding the following works for the latter scenario, but does nothing when in the inventory menu:

  self.Disable()
  Utility.Wait(1.0)
  self.Delete()

RemoveItem works while in the inventory menu, but I could not get it to work with Self, which means I would have to manually add a property for each book, and also somehow make sure that part doesn't fire when a book is activated outside the inventory menu.

 

I also tried keeping the book as an actual spell tome, having the player learn a dummy spell without effects and attaching the same script to it, with RemoveSpell DummySpell added so the book could be used again. This seemed really buggy however, in that sometimes the script would just not fire at all, and it displays an unwanted "spell learned" notification.

 

Is there any simple way to have the script remove the book it's attached to, no matter where/how the book is activated?

 

Alternatively, is there a way to detect if the book is being read from the inventory menu or outside it, so that the player would simply pick it up when activating it outside?

Link to comment
Share on other sites

 

 

Thanks, I tried using that, but it seems like activating a book enters menumode (presumably because the book opens) regardless of whether it's from the inventory or the world. Maybe there is a clever way to use it for my purpose, but so far I haven't come across any myself.

 

You could perhaps use BlockActivation OnInit or OnLoad. Then my theory is that it wouldn't open any menu when activated from the world but still run your script. Then it's at least possible to check for menu mode.

 

 

RemoveItem works while in the inventory menu, but I could not get it to work with Self, which means I would have to manually add a property for each book, and also somehow make sure that part doesn't fire when a book is activated outside the inventory menu.

Self refers to the objectreference, not the base object. You should use something like

actor player = Game.GetPlayer()
book myTome = self.GetBaseObject() as book
player.RemoveItem(myTome, 1, true)

So try checking whether the player is in menumode, and if yes, do the above, if no, disable and delete. It might work, it might not ;)

 

EDIT: Oh, I actually don't think you have to check for menu mode anyway. If you disable normal activation onInit or OnLoad, then the OnRead event might only be able to run from the inventory! That means that you can just have an OnRead event that removes the book from the inventory and an OnActivate event that disables and deletes the objectreference.

Edited by wormple12
Link to comment
Share on other sites

 

 

RemoveItem works while in the inventory menu, but I could not get it to work with Self, which means I would have to manually add a property for each book, and also somehow make sure that part doesn't fire when a book is activated outside the inventory menu.

Self refers to the objectreference, not the base object. You should use something like

actor player = Game.GetPlayer()
book myTome = self.GetBaseObject() as book
player.RemoveItem(myTome, 1, true)

 

Isn't the reference actually what I want to remove, since that is the instance of the base object being activated? Or am I misunderstanding how that works? I tried adding this, but it wouldn't remove the item from the inventory.

 

 

 

You could perhaps use BlockActivation OnInit or OnLoad. Then my theory is that it wouldn't open any menu when activated from the world but still run your script. Then it's at least possible to check for menu mode.

 

<snip>

 

EDIT: Oh, I actually don't think you have to check for menu mode anyway. If you disable normal activation onInit or OnLoad, then the OnRead event might only be able to run from the inventory! That means that you can just have an OnRead event that removes the book from the inventory and an OnActivate event that disables and deletes the objectreference.

 

 

You are absolutely right, BlockActivation() solves my initial problem! I'm fine with having to define book properties for each tome, so this is what I have right now:

Scriptname _SCR_ScrollTomeLarge extends ObjectReference

LeveledItem Property RandomScroll auto
Book Property ThisTome auto

Event OnLoad()
  BlockActivation(true)
EndEvent

Event OnActivate(ObjectReference ThisTome)
  self.Disable()
  Game.GetPlayer().AddItem(ThisTome, 1, true)
  Utility.Wait(1.0)
  self.Delete()
EndEvent

Event OnRead()
  Game.DisablePlayerControls(False, False, False, False, False, True)
  
  int ScrollAmount = Utility.RandomInt(5, 8)
  Game.GetPlayer().AddItem(RandomScroll, ScrollAmount)
  
  Game.GetPlayer().RemoveItem(ThisTome, 1, true)
  
  Game.EnablePlayerControls(False, False, False, False, False, True)
EndEvent

Now it appears as if you just pick up the tome when it's placed in the world and then activated, and when you use it from the inventory, it adds the scrolls and removes one copy of itself.

 

However, I just realized that it's possible to read a book while it's still in a container, which has presented kind of the same problem over again, except that this time it can't be solved by BlockActivation() ;) So I'm currently banging my head against how to find out if the book is in a container or not.

 

Thanks a lot for your help, it's greatly appreciated! Let me know if you have any ideas about containers as well :D

Edited by Merrymac
Link to comment
Share on other sites

I solved my container problem with SKSE, by adding a UI.IsMenuOpen("InventoryMenu") condition to the OnRead() event. Now it looks like this:

Event OnRead()
  if UI.IsMenuOpen("InventoryMenu")
    Game.DisablePlayerControls(False, False, False, False, False, True)
    
    int ScrollAmount = Utility.RandomInt(5, 8)
    Game.GetPlayer().AddItem(RandomScroll, ScrollAmount)
    
    Game.GetPlayer().RemoveItem(ThisTome, 1, true)
    
    Game.EnablePlayerControls(False, False, False, False, False, True)
  endif
EndEvent

If not in the player's inventory menu, it just plays the book opening animation instead. Perhaps not the most elegant solution, but it does the job \o/

 

Link to comment
Share on other sites

 

 

Isn't the reference actually what I want to remove, since that is the instance of the base object being activated? Or am I misunderstanding how that works? I tried adding this, but it wouldn't remove the item from the inventory.

Oh, yeah, the wiki actually states that it's also possible to remove an objectreference with RemoveItem -- I just didn't think so. My mistake. I don't understand why your RemoveItem(Self, 1) doesn't work for you then... you could perhaps try some papyrus mindgames and do pretty much the same with

objectreference myTome = self as objectreference

utility.wait(0.5)
player.RemoveItem(myTome, 1, true)

It shouldn't be necessary to fill every single tome in the game into a property no matter what.

 

Anyway, I also found that the wiki states this:

 

This function does not work on items pre-placed inside containers until that container is opened and its contents are finally loaded by the game.

That might mean that if you try to use RemoveItem on a book that you already have in inventory as you start the game (or if you try to read a book from another container object) then it can't run the function succesfully. The wiki has a solution for that but it looks like a bit of an overkill. Perhaps a necessary overkill though...

Link to comment
Share on other sites

Items do not have a "self" when they are in the inventory unless they are persistent.

Now that you say it, that does ring a bell.

 

@MerryMac, could you give us an overview of what you haven't been successful in doing yet that you want to do? We've taken a few subject leaps since the first post so I'm gradually getting a bit disoriented :sweat:

Edited by wormple12
Link to comment
Share on other sites

 

@MerryMac, could you give us an overview of what you haven't been successful in doing yet that you want to do? We've taken a few subject leaps since the first post so I'm gradually getting a bit disoriented :sweat:

 

It's actually coming along so far--The BlockActivation() part, together with being able to check for specific menus with SKSE should put the necessary constraints in place, so that the books in question won't trigger the OnRead() script from anywhere outside the player's inventory. So far I can't think of any other ways to access book contents in-game, at least.

 

It would have been ideal to not have to rely on SKSE and to not have individual book properties for every tome, but I've done worse mods :)

 

I've just started copying the scripts and adjusting the book properties for each tome (using TES5Edit, so it's not that bad) and will play with it for a while to test. Seems promising for now, thanks a lot for the help again! Glorious credit will be yours if it ends up on the nexus :D

Edited by Merrymac
Link to comment
Share on other sites

  • Recently Browsing   0 members

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