lesi123 Posted December 17, 2016 Share Posted December 17, 2016 (edited) I'm currently stress testing a script rewrite of my old combat mod and, in the beginning, if I engaged in fights with many NPCs or entered a situation with a lot of OnHit events flying around (dear god, the multi dragons fights...), Papyrus would start to churn out stack dumps. I rewrote my scripts again using states using the "Stopping a Function From Running Again Before Finishing" method detailed here: http://www.cipscis.com/skyrim/tutorials/states.aspx and made a Ready state and Busy state. It helped tremendously! However, I originally had the OnHit event check a few condition then go to a custom function block depending on what conditions were met. Now I'm not so sure if those function blocks should be in the Ready state or if they are fine outside. In essence, the code currently looks like this: State ReadyEvent OnHit GoToState ("Busy") ;don't run this OnHit event again until it's finished checking conditions if Blah == 1Function1()elseif Blah == 2Function2()endif GoToState("Ready") ;finished checking conditions and sent to function blocks, OnHit can be called again EndEventEndState State Busy Event OnHit; nothingEndEvent EndState Function1() if Foo == 1Bar1.cast(PlayerRef)elseif Foo == 2Bar2.cast(PlayerRef)endif EndFunction Function2() if Doo == 1Dad1.cast(PlayerRef)elseif Doo == 2Dad2.cast(PlayerRef)endif EndFunction Any advice on where I should be placing these functions blocks? Do I even need them?... Thank you for taking the time to read this and any help you could provide! Edited December 17, 2016 by lesi123 Link to comment Share on other sites More sharing options...
cdcooley Posted December 17, 2016 Share Posted December 17, 2016 That structure is fine. The helper functions can be declared at the top level. The important part is that in your codes isn't switching back to Ready until after those functions finish running and give control back to the OnHit event that called them. Link to comment Share on other sites More sharing options...
lesi123 Posted December 17, 2016 Author Share Posted December 17, 2016 I'm unsure what you mean be declaring the functions at the top, you mean as in writing them above the OnHit event? I'm honestly not sure if it's switching back to the Ready state before the functions are finished running in the current structure. I only know that the "bottleneck" condition checks are but wasn't sure if it was going to be a problem written the way it is or if I had to shove those functions into the Ready state too to avoid the functions running during or after the script gets sent back to Ready. Link to comment Share on other sites More sharing options...
NexusComa Posted December 17, 2016 Share Posted December 17, 2016 (edited) Int QF = 0 ; <- flag Event OnInit() GoToState("Done")EndEventv Event OnHit() If(QF==(0)) ; <- is ready QF = 1 ; <- set busy If(Foo==(1)) self.Function1() ElseIf(Foo==(2)) self.Function2() EndIf QF = 0 ; <- reset ready EndIfEndEvent Function1() ;bla bla blaEndFunction Function2() ;bla bla blaEndFunction State Done ; do nothingEndState ;-------------------- This is weird ... ScriptName LongFunctionScript extends ObjectReference Function LongFunction() GoToState("Busy") ; Don't run this function again until it's finished ; Do something that takes a long time GoToState("Waiting") ; The function has finished, so it can be called againEndFunction State Busy Function LongFunction() ; Do nothing EndFunctionEndState Edited December 17, 2016 by NexusComa Link to comment Share on other sites More sharing options...
lesi123 Posted December 17, 2016 Author Share Posted December 17, 2016 I've seen both of those methods and for what I'm wanting, both would work. I just know states were already part of the language and can be used in more creative ways if I ever wanted to do crazier things down the road. Link to comment Share on other sites More sharing options...
cdcooley Posted December 17, 2016 Share Posted December 17, 2016 Int QF = 0 ; <- flag SmkViper, one of the developers responsible for Papyrus, has said that using states allows the game to optimize scripts. And that using flags should only be done in the rare cases where states can't accomplish what you want. (Even then a combination of states and flag variables would be the right solution.) While using states appears equivalent to what you could accomplish more easily using a variable as a flag there are extremely important optimizations that you can't implement using flag variables. Especially for OnHit and inventory events the flag variable method is a terrible idea. The state solution prevents LongFunction from even getting called when the Busy state is active. Using a flag variable still results in LongFunction getting called and the if statement running every time with only the body of the if getting skipped. For something like the OnHit or inventory events (which can get called hundreds of times per second) that difference really matters. ScriptName LongFunctionScript extends ObjectReference {This is the optimal Papyrus way to ensure multiple copies of a function or event don't run at the same time.} Function LongFunction() GoToState("Busy") ; Do something that takes a long time GoToState("") EndFunction State Busy Function LongFunction() EndFunction EndState ScriptName LongFunctionScript extends ObjectReference {This is a bad idea. Always use the state system above instead!} string busy = false; Function LongFunction() if !busy busy = true ; Do something that takes a long time busy = false endif EndFunction Link to comment Share on other sites More sharing options...
cdcooley Posted December 17, 2016 Share Posted December 17, 2016 I'm unsure what you mean be declaring the functions at the top, you mean as in writing them above the OnHit event? I'm honestly not sure if it's switching back to the Ready state before the functions are finished running in the current structure. I only know that the "bottleneck" condition checks are but wasn't sure if it was going to be a problem written the way it is or if I had to shove those functions into the Ready state too to avoid the functions running during or after the script gets sent back to Ready. When I said top level I meant outside of any declared state just like in the code you posted above. The only thing that needs to be protected is the OnHit event itself and you're doing it right. When you call Function1, Function2, etc. from inside the OnHit event the control is transferred to those functions and that instance of OnHit is on hold until those functions return, so the state change at the bottom of OnHit won't happen until after those other functions return. Link to comment Share on other sites More sharing options...
Masterofnet Posted December 17, 2016 Share Posted December 17, 2016 What about this? ScriptName LongFunctionScript extends ObjectReference State Active Function LongFunction() GoToState("Busy") ; Do something that takes a long time GoToState("Active") EndFunction State Busy ; Do nothing EndState Link to comment Share on other sites More sharing options...
NexusComa Posted December 17, 2016 Share Posted December 17, 2016 (edited) Programming without flags .... Madness! The default Skyrim scripts are filled with flags of every type. Even the Global's are used as flags. The optimizing comes into play as the current state is loaded on startup and ready to go in its entirety.Buffered if you will ... This had been completely considered and fully optimized by using the GotoState("Done"). The only time this method would run into problems with an OnHit event would be if the script used the wait commandsomeplace within the function. Then it could miss a hit. Other than that it could take 101 hits a second and never miss. Considering we can't really see the script and have no clue to what it's doing to respond at all is kind of hit and miss.I suggest some testing to see how/if this will work for you in this case. [ If you have a way to write this script simply post it for his consideration ... why feel the need to troll others responses ] Edited December 18, 2016 by NexusComa Link to comment Share on other sites More sharing options...
cdcooley Posted December 18, 2016 Share Posted December 18, 2016 Programming without flags .... Madness! The default Skyrim scripts are filled with flags of every type. Even the Global's are used as flags. The optimizing comes into play as the current state is loaded on startup and ready to go in its entirety.Buffered if you will ... This had been completely considered and fully optimized by using the GotoState("Done"). The only time this method would run into problems with an OnHit event would be if the script used the wait commandsomeplace within the function. Then it could miss a hit. Other than that it could take 101 hits a second and never miss. Considering we can't really see the script and have no clue to what it's doing to respond at all is kind of hit and miss.I suggest some testing to see how/if this will work for you in this case. [ If you have a way to write this script simply post it for his consideration ... why feel the need to troll others responses ]I am not trolling your response, the original poster already has a working script and was simply asking if it was really the correct solution to the problem. And as I've stated repeatedly now, it is. I've cited my source: a developer of the scripting language. You also provided the same basic state information at the bottom of your first posting, but called it weird. Since you also posted code which does not solve the problem of the scripting system getting overwhelmed by too many calls to OnHit I felt the need to clarify that the flag variable scheme something that SmkViper said should not be used as a general solution when states will do the job. Flag variables are indeed used in many of the game's scripts, but not for the sort of optimization which is the topic of this thread. Link to comment Share on other sites More sharing options...
Recommended Posts