GoToState not working, state does not change

Post » Tue Nov 20, 2012 2:28 am

Hi all,

I have been searching the net and through forums but I have not found anything that appears to address the issue I am having. According to all the examples and tutorials I have gone through the below script should work and the "state" of the event should change from one to the other. The issue I am seeing is that in game the only state that ever gets executed is the "Auto" one. As far as I understand, I don't need to declare an empty state since this is extends a base script. Can anyone see an issue with this script or explain why it is does change state in game? Thanks for any help.


Scriptname MagicEffectPayMagickaDebit extends ActiveMagicEffect
{This script debits and refunds a Magicka debit for a toggle effect.}

MagicEffect Property akEffect Auto
Float Property akEffectCost Auto

State Refund
Event OnEffectStart(Actor akTarget, Actor akCaster)
Game.GetPlayer().ModActorValue("Magicka", akEffectCost)
Debug.Notification( akEffectCost + " Magicka refunded to player for " + akEffect.GetFormID())
GoToState("PayCost")
EndEvent
EndState


Auto State PayCost
Event OnEffectStart(Actor akTarget, Actor akCaster)
Game.GetPlayer().ModActorValue("Magicka", akEffectCost * -1)
Debug.Notification("Player pays initial Magicka debit for " + akEffect.GetFormID())
GoToState("Refund")
EndEvent
EndState
User avatar
MarilĂș
 
Posts: 3449
Joined: Sat Oct 07, 2006 7:17 am

Post » Tue Nov 20, 2012 1:04 am

just to make it easyer for us to read :)
Scriptname MagicEffectPayMagickaDebit extends ActiveMagicEffect{This script debits and refunds a Magicka debit for a toggle effect.}MagicEffect Property akEffect AutoFloat Property akEffectCost AutoState RefundEvent OnEffectStart(Actor akTarget, Actor akCaster)Game.GetPlayer().ModActorValue("Magicka", akEffectCost)Debug.Notification( akEffectCost + " Magicka refunded to player for " + akEffect.GetFormID())GoToState("PayCost")EndEventEndStateAuto State PayCostEvent OnEffectStart(Actor akTarget, Actor akCaster)Game.GetPlayer().ModActorValue("Magicka", akEffectCost * -1)Debug.Notification("Player pays initial Magicka debit for " + akEffect.GetFormID())GoToState("Refund")EndEventEndState
User avatar
Soph
 
Posts: 3499
Joined: Fri Oct 13, 2006 8:24 am

Post » Tue Nov 20, 2012 10:05 am

States do not persist across multiple instances of a script. From what you posted, it looks like your spell gets called multiple times, once to turn it on and debit Magicka, and again to turn it off and refund it. Each time, the script is initialized anew, jumps to the Auto state, processes the event, then ends, losing the state value.

For what you are trying to do, you would probably better off creating an Ability spell that does the debit in onEffectStart and the refund in onEffectFinish, and a second spell which checks if the player has the spell and removes it if it is present, or adds it if it is not. This is how most toggle spells work.
User avatar
Baby K(:
 
Posts: 3395
Joined: Thu Nov 09, 2006 9:07 pm

Post » Mon Nov 19, 2012 10:58 pm

States do not persist across multiple instances of a script. From what you posted, it looks like your spell gets called multiple times, once to turn it on and debit Magicka, and again to turn it off and refund it. Each time, the script is initialized anew, jumps to the Auto state, processes the event, then ends, losing the state value.

For what you are trying to do, you would probably better off creating an Ability spell that does the debit in onEffectStart and the refund in onEffectFinish, and a second spell which checks if the player has the spell and removes it if it is present, or adds it if it is not. This is how most toggle spells work.

Oh, you beat me to answering...
User avatar
Avril Churchill
 
Posts: 3455
Joined: Wed Aug 09, 2006 10:00 am

Post » Tue Nov 20, 2012 10:14 am

First, thank you very much for reply.

Just to make sure I am understanding this correctly below is how I thought they worked based on examples.

Your description make me think that all the "state" mechanism does is give you a fancy DOS "goto" statement to bypass logic. In an example I saw they used a light switch. Click the activator it turns on, click again it turns off. This was all done in one script just declaring the event under two different states. The first time they click on the activator that is one instance and it set the state to "on". The instance is dropped from memory once the script is finish. The second time they click on the activator its a new instance but how does the script know what state it is in if it is not persisted? They were not doing any check on the light to see if it was on.

Below is the example I am referring to and thanks again for helping me to understand this.


ScriptName LightSwitchActivateOrHit extends ObjectReference
{ When this switch is activated or hit, switch its associated lights on or off }

ObjectReference Property MasterLight Auto
{ The "Master" light, which is the enable parent of all other lights that should be affected by this switch }

Function Toggle()
; Empty function definition to allow Toggle to be defined inside states
EndFunction

Auto State Off

Event OnBeginState()
MasterLight.Disable()
EndEvent

Function Toggle()
GoToState("On")
EndFunction

EndState

State On

Event OnBeginState()
MasterLight.Enable()
EndEvent

Function Toggle()
GoToState("Off")
EndFunction

EndState



Event OnActivate(ObjectReference akActionRef)
Toggle()
EndEvent

Event OnHit(ObjectReference akAggressor, Form akSource, Projectile akProjectile, bool abPowerAttack, bool abSneakAttack, bool abBashAttack, bool abHitBlocked)
Toggle()
EndEvent
User avatar
Amysaurusrex
 
Posts: 3432
Joined: Wed Aug 09, 2006 2:45 pm

Post » Tue Nov 20, 2012 7:21 am

The activator script is not dropped from memory like a spell's script is.

States attached to objects (like activators) persist in memory or your save game as long as they are loaded. To put it another way, think of scripts as being attached to an object's FormID. When you encounter an activator, states work like you're expecting because it has only a single FormID which does not change. Even if you leave the area it is in, the state data persists in your save game data (ever wonder why your save game file gets bigger and bigger the more you play?).

Casting a spell, though, creates an instance of that spell with a unique (and temporary) FormID that is deleted again at the end of its duration. You can see this for yourself by casting a fireball and, before it hits anything, popping open the console and clicking on it in midair. You'll get a FormID of FFsomething. Now close the console, cast another one, repeat the process: different FormID. (Edit: This is actually the FormID of the Fireball spell's Projectile or Explosion instance, but the principle is the same)

The same is true of any spell that is cast: that spells's state data is maintained only for the duration of that spell.

With the solution I offered, you are using an single Ability-type spell, added to the player using AddSpell. Ability spells can have an infinite duration, so its FormID never changes and its OnEffectFinish event only fires when it is dispelled or removed by another script. That is why you can use states safely in an Ability.

As for States working like a Goto, that is not correct. A State is simply an alternate set of functions and events for a Form. GotoState doesn't halt execution of the current event or function (although it does begin the execution of relevant OnBeginState/OnEndState events), it just ensures that the next time that event or function is called, the version from the target state is run. For that reason, when using states it's often better to put the GotoState line at the beginning of the code you intend to use, so if the event gets triggered more than once in rapid succession (like the OnHit event) you can be sure that every trigger after the first occurs in the State you intend.
User avatar
Jordan Moreno
 
Posts: 3462
Joined: Thu May 10, 2007 4:47 pm

Post » Mon Nov 19, 2012 8:34 pm

OMG thank you so much Verteiron! This totally and finally makes since to me. Thank you for taking the time to provide such detail. I'm sure this post will help any future modders when they search for this type of scenario. You have saved me from pulling out the rest of my hair. :biggrin:
User avatar
James Baldwin
 
Posts: 3366
Joined: Tue Jun 05, 2007 11:11 am

Post » Tue Nov 20, 2012 8:29 am

Happy to help!
User avatar
Nikki Lawrence
 
Posts: 3317
Joined: Sat Jul 01, 2006 2:27 am


Return to V - Skyrim