OnUpdate Not Called after RegisterForSingleUpdate

Post » Sat Nov 17, 2012 6:02 pm

I am relatively new to the CreationKit but have started on a dungeon I'd like to develop for Skyrim. I understand that my problem is almost definitely an understanding problem and I know there are other ways that I could achieve this, but I'd like to understand what's going on, and also to do this a good way and not bodge it.

Goal: So, my goal is to add a book to the game and when you pick that book up - the book will cast a spell on the carrier (only if the carrier is the player) every X seconds. The book is actually one of the traps that will exist in my dungeon - so taking the book will cast a damaging spell on the player ever few seconds until they drop it (at which point it teleports back to it's original position). The book IS safe to read however.

Script: The following script is commented, but I go over my expected and seen outcomes below.

Scriptname JWTrappedBookScript extends ObjectReference  ; PropertiesSpell Property SpellTrap Auto			 ; Cast this spell every X seconds on the player when this book is in their possession.Float Property PulseRate = 0.5 Auto		; The amount of time to wait between casting this spell.; Private VariablesBool IsPlayerCarrying = False			; Is the player currently carrying this book?; When the player picks up this book, do a meaty amount of damage to them.Event OnContainerChanged(ObjectReference akNewContainer, ObjectReference akOldContainer)	if akNewContainer == Game.GetPlayer()		IsPlayerCarrying = True;			; The player is now carrying this book		ZapPlayer();						; Zap the player straight away - if they are of a high level, they might survive.		RegisterForSingleUpdate(PulseRate)		; Start zapping the player	else		UnregisterForUpdate();		IsPlayerCarrying = False;			; The player is no longer carrying this book		Self.MoveToMyEditorLocation();		; Always returns to starting position	endifEndEvent; Update every 'PulseRate' secondsEvent OnUpdate()	Debug.Trace("JWTrappedBookScript: OnUpdate Called.")	ZapPlayer()	if ( IsPlayerCarrying )		RegisterForSingleUpdate(PulseRate)	EndIfEndEvent; This is the function we call to zap the playerFunction ZapPlayer()	SpellTrap.Cast(Self, Game.GetPlayer())	Debug.Trace("JWTrappedBookScript: Just zapped player.")EndFunction

Expected Outcome: I would expect the above script to zap the player immediately after they pick up the book and then every 'PulseRate' seconds afterwards, until the book is dropped or the player dies.

Actual Outcome: The player does get zapped with the expected spell when they first pick up the book, but the OnUpdate event is never called. So they only get zapped once.

I've tried all sorts - hard-coding the PulseRate to 0.5, adding Disable(), Enable() calls at random places. Any help would be greatly appreciated, especially as it will help me to better understand the role of the OnUpdate method in an object's lifecycle. I understand that I could probably start a MagicEffect on the player in this case, but I like the idea of re-using this script to add other traps to future books (and indeed, any item you can pickup I guess!)
User avatar
Crystal Clear
 
Posts: 3552
Joined: Wed Aug 09, 2006 4:42 am

Post » Sat Nov 17, 2012 4:52 pm

How are you testing?

If it's with an old save that has already seen previous incarnations of your script then it might be that the old scripts "ghost" is playing tricks on you. Coc'ing straight from the main menu is a good way to give your mod a quick test but a genuine clean save (helgen completed, no mods running) is always preferable.

If you are in fact testing with a clean save then I can't really help you any further because I cannot see anything wrong with your script at first glance. But because I'm only learning papyrus myself, I wouldn't be surprised if somebody else comes along and finds the problem

- Hypno
User avatar
Mike Plumley
 
Posts: 3392
Joined: Wed Sep 05, 2007 10:45 pm

Post » Sat Nov 17, 2012 8:50 pm

It may be because the "if isPlayerCarrying" may require an "== true" after it.. only my best guess as it it looks like it should work otherwise.

That said, I think you can remove the zapPlayer() from the onContChanged event, since it's run in the onUpdate. Also, you don't need to unRegisterForUp since you're using regForSingle. So I would try adding the true condition, and deleting those other two lines, and see if it works (or at least fires your zap once).

If not, I've seen strange things happen when multiple scripts are attached to the same object/ref.. especially regarding regForSingle timing. I fixed it by doing what you already do though.. by using a named variable instead of a number (and having that named var something unique for each different script).

[EDIT: then of course I agree with hypno88... it could just be a dirt save messing with you]
User avatar
Justin
 
Posts: 3409
Joined: Sun Sep 23, 2007 12:32 am

Post » Sat Nov 17, 2012 8:50 am

Now I'm rubbish at reading other people's scripts.

But what is the Update being registered on, the book, or the player inventory?

If it is on the container, then it is only going to zap once - as the book only changes container once (when it is first picked up). After that it is just sitting in the same container.

(as in, the RegisterForSingleUpdate is in the Container-Changed Event script ... and that event only occurs the once)

... maybe it's me ... usually is ...

(and, dirty save is likely ... so load game to main menu and COC to a cell to test from a guaranteed "new" game)
User avatar
Franko AlVarado
 
Posts: 3473
Joined: Sun Nov 18, 2007 7:49 pm

Post » Sat Nov 17, 2012 3:42 pm

h4vent: Given that the "isPlayerCarrying" returns true, the onUpdate event is self-perpetuating by firing another regForSingle. That's why I think the syntax may require actually saying "if isPlayerCarrying == true" to work - otherwise it would only fire once and be done.
User avatar
Pumpkin
 
Posts: 3440
Joined: Sun Jun 25, 2006 10:23 am

Post » Sat Nov 17, 2012 7:02 pm

Thanks everyone for all of your responses - I'm hoping once I crack this I'll be able to add a whole bunch of effects to our mod. I've only had a few minutes this morning to try to resolve the issue, but haven't managed to yet - unfortunately, this pesky work thing has started now so I'll have to look after work!

Hypno88: I didn't realise you can just 'COC' straight from the main menu without having to load a save first. That's really useful. Now that I've disabled the intro video and with that trick, I can test out my changes that don't play nice with the hot loading much quicker. Unfortunately, in the few minutes I had this morning, even loading it straight from the main menu (so, presumably the save was completely clean) the ZAP only fired once (onContainerChange) and not onUpdate still.

SLuckyD: Just to be extra careful, I added '== true', but unfortunately the OnUpdate isn't even being called the first time, so if I remove the Zap() method from the onContainerChanged event then it never gets called within the script.

h4vent: I've just added Self to make the call 'Self.RegisterForSingleUpdate()' so that it explicitly calls the book itself. Unfortunately, this still doesn't seem to fix the issue.

Thanks again for all of your help guys, I'll run through the suggestions one more time after work before looking for other possible solutions. I think my current line of thinking is that the book might be 'deactivated' some how when it is put into the player's inventory, and so it's OnUpdate method is never called after it's sitting in the inventory. If that's the case - that seems quite strange!
User avatar
Jaki Birch
 
Posts: 3379
Joined: Fri Jan 26, 2007 3:16 am

Post » Sat Nov 17, 2012 9:30 pm

I think my current line of thinking is that the book might be 'deactivated' some how when it is put into the player's inventory, and so it's OnUpdate method is never called after it's sitting in the inventory. If that's the case - that seems quite strange!

That would be my guess. See http://www.gamesas.com/topic/1393383-objectrefs-containers-onitemremoved-lvd-items/page__p__21162940__hl__items%20containers__fromsearch__1#entry21162940 thread, for example.
User avatar
tegan fiamengo
 
Posts: 3455
Joined: Mon Jan 29, 2007 9:53 am

Post » Sat Nov 17, 2012 7:48 pm

Many thanks Ingenue for setting me down the path which I think explains this situation. From my readings across the web, It appears that when the item enters your inventory it no longer 'exists' as an instance of that item - I presume it is merely a reference in your pack to an item of that type, which is then 'recreated' when it's taken outside of your inventory. It looks like I may have to go around the houses after all. I've managed to get the effect I want in a different way - while it seems a lot more messy, it may be the only way to get this to work!

1. I duplicated one of the FrostDamage Magic Effects and made it target self (instead of a target). Thus, a creature with this effect on takes X damage per second continuously and is slowed.

2. I then created my own 'Spell', which is of the Ability type. This means I can use AddSpell and RemoveSpell to apply this ability to a player without having to 'cast it'. This is like granting Resist Frost or some other such ability to a player. This spell as the above Magic Effect as the MagicEffect for the spell.

3. The book script now simply applies the magic effect when you pick up the book and removes it when you drop the book, as so.

Scriptname JWTOHBookEntranceScript extends ObjectReference  ; PropertiesSpell Property TrapSpell Auto                             ; The spell to cast when item is added to inventory.EffectShader Property TeleportEffect Auto;        ; The effect shader to play when the object teleports back to its starting location.; When the player picks up this book, add TrapSpell to the player and remove it when they drop the book.Event OnContainerChanged(ObjectReference akNewContainer, ObjectReference akOldContainer)    if ( akNewContainer == Game.GetPlayer() )        Game.GetPlayer().AddSpell(TrapSpell, false);     ; Add a debilitating spell with a constant damage effect    else        Game.GetPlayer().RemoveSpell(TrapSpell);        ; Remove the debilitating spell        TeleportEffect.Play(Self, 3.0);        Self.MoveToMyEditorLocation();                           ; Always returns to starting position    endifEndEvent

I guess there is no reason why I couldn't also then write an ActiveMagicEffect script that that an OnUpdate event that is called when that magic effect is active on a player to do what I was originally going to do (cast an damaging frost spell on the owner of the book).

Many thanks for all of your help everyone. And Ingenue, I hope the weather is as good down in Kernow as it is here!
User avatar
Rachael Williams
 
Posts: 3373
Joined: Tue Aug 01, 2006 6:43 pm

Post » Sat Nov 17, 2012 5:03 pm

Absolutely glorious. :celebration: Glad you got it sorted!
User avatar
Kit Marsden
 
Posts: 3467
Joined: Thu Jul 19, 2007 2:19 pm

Post » Sat Nov 17, 2012 6:11 am

Hypno88: I didn't realise you can just 'COC' straight from the main menu without having to load a save first. That's really useful. Now that I've disabled the intro video and with that trick, I can test out my changes that don't play nice with the hot loading much quicker. Unfortunately, in the few minutes I had this morning, even loading it straight from the main menu (so, presumably the save was completely clean) the ZAP only fired once (onContainerChange) and not onUpdate still.
Just FYI, but this is a bad way to test things. When you coc from the main menu, you're not even a real character yet, just a shell of what should become one. I don't even know why Bethesda made that possible because it wasn't in any previous game.

If you're looking for a clean method for testing, make a save on a purely vanilla game right outside the cave you exit from Helgen. Load that save whenever you want to test something in your mod because then all the expected underlying variables for things will be active and you'll have a more reliable environment that's properly interacting with the world.

I strongly suspect that a lot of the bugs the game has (and mods too) are because people at Bethesda insisted on testing this way rather than with a dedicated clean save.
User avatar
Rudy Paint fingers
 
Posts: 3416
Joined: Sun Nov 11, 2007 1:52 am

Post » Sat Nov 17, 2012 8:44 pm

I agree that a mod should be completely tested using a plain, clean saveGame. But using coc from the Main/Title Screen is GREAT for prelim testing and minor tweaking.. very fast for jumping in and out. Just keep in mind that there will be a BUNCH of errors in your Papyrus log from it, and depending on what you're modding, several things will cause drama or simply not be there. I used it all the time for the remote areas that I mod (I try to avoid modding 'high risk potential' areas); but I ALWAYS thoroughly test it properly after those early stages and before releasing.
User avatar
Sanctum
 
Posts: 3524
Joined: Sun Aug 20, 2006 8:29 am

Post » Sat Nov 17, 2012 5:52 pm

@Arthmoor: Ahh, thanks for that info. While I've played with Elder Scrolls modding since the Oblivion days, I've never got that far into it. I think my current dungeon is probably the mod most likely to see release - so the additional tips on 'doing it properly' are great to read.

@SluckyD: Yeah, even though I know it's a bit wrong, I think I'll probably still use it for initial testing of the geometry, bad-guys and initial scripts for a level ;). But yeah, I'll definitely be sure to test it out the proper way when it's closer to being finished.

What would be really useful is if there was a command line parameter I could use to load Skyrim directly into a loaded game from a save. Skipping the launcher would be nice too, like you could with Oblivion!
User avatar
Del Arte
 
Posts: 3543
Joined: Tue Aug 01, 2006 8:40 pm


Return to V - Skyrim