Reidlos Posted May 31, 2018 Share Posted May 31, 2018 Hi, im new to scripting in general, i get a good jist and slowing picking up more and more but, I was wondering if there is away to do what would be 30 if statements in only a few lines? I want to keep checking that a condition is true ever x seconds up to 10 minutes. I have/want to do this because if the condition is broken in that time frame, it restarts. If condition1 && condition2Utility.Wait(x seconds) If condition1 && condition2 (are still correct) Utility.Wait(x seconds) (up till 10 mins) this repeats) if condition1 && condition2 (correct with out fail durring time frame) dothethingineeddone EndIf EndIfEndIf If i break it up like this 10 mins, I could do it in 20 second chunks (prefer 10 seconds but) but thats a lot of IFs is there a way to keep checking conditions for a 600 second time frame every 10 seconds? EDIT/UPDATE: was thinking (would have to figure out how to write it out but...) If conditions are trueincrease a count int by 1then wait x seconds THEN repeat else if conditions fail reset count when count is 10do thing i need and end event is that somthing that will work? any examples? Link to comment Share on other sites More sharing options...
RichWebster Posted May 31, 2018 Share Posted May 31, 2018 (edited) You could try a while loop. while SomeCondition == true; always check here ; do stuff like wait endwhile ; Script continues when condition no longer true. Be mindful that a while loop can run forever, so make sure your condition check works properly and the script ends when it should do. Use some Debug.Notification() calls. Edited May 31, 2018 by B1gBadDaddy Link to comment Share on other sites More sharing options...
foamyesque Posted May 31, 2018 Share Posted May 31, 2018 There's a few possible techniques; a while loop and counter arrangement, for example, or alternatively chained updates: int iChecksLeft = 20 ;replace with how many times you want to check the conditions Event OnUpdate() bool bConditions = false ;replace with whatever your conditions for continuing the loop are float fTime = 30.0 ;replace with your desired polling interval if bConditions iChecksLeft-=1 RegisterForSingleUpdate(fTime) endif if iChecksLeft > 0 else ; whatever you want to have happen after ten minutes endif EndEvent Link to comment Share on other sites More sharing options...
greyday01 Posted May 31, 2018 Share Posted May 31, 2018 Can someone explain what registerforupdate versions do. I've never used them because I haven't needed them (at least I hope not), but I don't understand them either. The wiki wasn't completely clear to me. A lot of the entries seem written by and for computer programmers who can probably make sense of those explanations. Link to comment Share on other sites More sharing options...
Evangela Posted May 31, 2018 Share Posted May 31, 2018 (edited) Can someone explain what registerforupdate versions do. I've never used them because I haven't needed them (at least I hope not), but I don't understand them either. The wiki wasn't completely clear to me. A lot of the entries seem written by and for computer programmers who can probably make sense of those explanations.RegisterForUpdate = Unsafe, requires UnregisterForUpdate, otherwise will break saves when the mod is uninstalled. The update will keep running until unregistered.RegisrerForUpdateGameTime = No one reported that it carries the same issues as the above. Requires UnregisterForGameTime. The update continues until unregistered.RegisterForSingleUpdate = safe to use, does not require unregistering, will not break saves when the mod is uninstalled. Runs exactly once when called.RegisterForSingleUpdateGameTime = same as above, runs exactly once. To be on the safe side, use only the RegisterForSinglexxx versions. By running, I mean OnUpdate() and OnUpdateGameTime(). These events keep going on forever when registered with the first two. For OnUpdate this is especially dangerous. It's basically a while loop that wasn't coded to end. As for the topic at end, if you're faced with a long if chain of any kind, it should probably be in a loop. Though it's not always possible depending on the type of checking. Wishing papyrus had switches and for-loops. Edited May 31, 2018 by Rasikko Link to comment Share on other sites More sharing options...
foamyesque Posted May 31, 2018 Share Posted May 31, 2018 (edited) Offhand, I can't think of any loop scenario that Papyrus' while loop can't perform. I'd like some version of a switch, but that's just a more syntactically compact version if/elseif/elseif/etc anyway, so it's not really needed. greyday01:Registration is a programming concept where some entity tells another entity about itself and, often, asks for something back. It's implemented when you have something that's handling information but doesn't know in advance who might be requesting what data, or when. In this case, what RegisterForUpdate (and variants) are doing is telling the game engine "I want you to give me an Update event in X time". It is implemented this way so that only things that actually *want* to get Update events will be sent them, significantly reducing the load on the engine, and also allowing for you to specify the time. For another example, SKSE allows you to register for various other things, such as menus or keystrokes, and so only the scripts that have actively requested that information will need to be sent it and the game itself need only check for the registered items instead of all possible ones. And, for a third example, in some of my own scripts, I have a set of aliases that need to have batch jobs run on them, but they're scattered across a number of modular quests. The aliases register themselves with a central processing quest when they're filled, which, with that accurate list, can do things like catch an MCM change and tell the aliases to update themselves appropriately. As to what the specific functions do: RegisterForUpdate(fTime) will ask the game engine to send an OnUpdate() event to your script every fTime real-world seconds (as long as game time is actually passing: most menus freeze it. The CK wiki can tell you more about which is which). This chain will not stop until and unless an UnregisterForUpdate() function is called, even if the script itself is removed from the game. It is therefore not recommended for use in mods. RegisterForUpdateGameTime(fTime) functions much the same way, and has the same problems. The primary differences are: 1. that it will send OnUpdateGameTime() events instead of OnUpdate(). That allows you to use both kinds of updates simultaneously without trampling on each other; and, 2. The time is measured in game time hours instead of real world seconds, so it's sensitive to timescale setting and things like the player waiting or sleeping. If a player waits or sleeps long enough for several OnUpdateGameTime() events to be sent, say if you asked for one every hour, only one update will trigger, once they're done and play resumes. The RegisterForSingle versions, on the other hand, will only give you *one* update of the chosen kind and no more. You can then, in the OnUpdate events, call RegisterForSingleUpdate() again, which will continue the chain if you need to. This is the preferred way to use these events because if the OnUpdate() event is no longer there to ask for the next update, the entire chain will stop and reduce the load on the engine. I use RegisterForSingleUpdate() and OnUpdate() extensively, for three major use cases: 1. Shifting execution of startup and initialization routines out of an OnInit() event. For basic stuff this isn't an issue but if you're doing stuff that takes time or if you want to make sure you can't access the data of something *else* that's still initializing it's useful. 2. Multithread processing. Say you've got fifty aliases you need to do the same thing to -- let's say equipping some item. You could just iterate through them and call GetActorRef().EquipItem(), but that will do each one in sequence and needs to wait for the completion of both the GetActorRef() and EquipItem() functions for each one. The net result is that the player will see the fifty people equipping things one after another, because EquipItem() is slow. Instead, though, what you can do is have an OnUpdate() event in a script on the aliases, which contains the EquipItem() call. Now, your main script still needs to iterate through the aliases, but all it needs to call is RegisterForSingleUpdate(0.1), which is blazingly fast by comparison. All of the aliases will then get an OnUpdate event and call EquipItem() more or less simultaneously. Not only does this mean you get the results together -- which is often desirable -- it also tends to be faster overall to boot. If you want to do more than one thing, some flags can be set to allow the OnUpdate event to know which thing to chose. If you're interested I can post my code for setting this up. 3. Long latency polling or processing. A while loop with a wait *can* do this, but the trouble is that it keeps whatever called it as an active script for as long as it's sitting there waiting away. That has performance impacts on everything else that tries to use the script engine. A RegisterForSingleUpdate()/OnUpdate() chain, on the other hand, will only be active for the time it takes to execute whatever processing needs to be done on that iteration. Edited May 31, 2018 by foamyesque Link to comment Share on other sites More sharing options...
RichWebster Posted May 31, 2018 Share Posted May 31, 2018 (edited) Point 3 would mean my while loop suggestion isn't ideal. In my use case of essentially the same code I gave in an example, the loop only runs for a couple of seconds in total runtime, but loops several times. That's enough for me. If you're using Wait() or other functions that will block the script, registering for an update is the best option. I don't want to confuse you but if there's several stages to your flow you could make use of script States to keep each OnUpdate() event separate, rather than having a bunch of if statements in one big one. Edited May 31, 2018 by B1gBadDaddy Link to comment Share on other sites More sharing options...
Reidlos Posted May 31, 2018 Author Share Posted May 31, 2018 Im looking into Registerforsingleupdate and while loop but for registerforsingleupdate, it uses a event (on update) The script im modifying uses a Event oneffectstart can 2 events work together? in other words can you nest events? Maybe I dont have too but just checking. Here is the script im trying to tweek, its a modded script to a mod, so im trying to keep it as true to its self as possible, with no additional changes elsewhere. its pretty simple for this test case Event OnEffectStart(Actor akTarget, Actor akCaster) PlayerRef = AkTarget If PlayerRef.HasEffectKeyword(aleInjuryKW) Utility.Wait(5.0) If PlayerRef.HasEffectKeyword(aleInjuryKW) && PlayerRef.GetActorValuePercentage("Health") > 0.99 Utility.Wait(5.0) If PlayerRef.HasEffectKeyword(aleInjuryKW) && PlayerRef.GetActorValuePercentage("Health") > 0.99 Utility.Wait(5.0) If PlayerRef.HasEffectKeyword(aleInjuryKW) && PlayerRef.GetActorValuePercentage("Health") > 0.99 Utility.Wait(5.0) If PlayerRef.HasEffectKeyword(aleInjuryKW) && PlayerRef.GetActorValuePercentage("Health") > 0.99 Debug.Notification("Injuries Healed") ale_InjuryFullHeal.cast(PlayerRef) EndIf EndIf EndIf EndIf EndIf EndEvent as my question said, im trying to find a good way to expand that 20 seconds with conditions true, to 10 mins with conditions true (which checking occasionally like 10 -20 seconds due to the much longer time frame) could i merge in or nest in somthing like @foamyesque said? int iChecksLeft = 20 ;replace with how many times you want to check the conditions Event OnUpdate() bool bConditions = false ;replace with whatever your conditions for continuing the loop arefloat fTime = 30.0 ;replace with your desired polling interval if bConditions iChecksLeft-=1 RegisterForSingleUpdate(fTime)endif if iChecksLeft > 0else ; whatever you want to have happen after ten minutesendif EndEvent I feel like I could just replace the OnEffectStart Event, with the OnUpdate one because one of the conditions is having said effect BUT not sure Link to comment Share on other sites More sharing options...
Reidlos Posted June 1, 2018 Author Share Posted June 1, 2018 so this script didnt work sadly, I think its because its designed more for the origional Event OnEffectStart what triggers an Event OnUpdate()? Scriptname HealInjury extends activemagiceffectSPELL Property ale_InjuryFullHeal AutoActor Property PlayerRef AutoKeyword Property aleInjuryKW Autoint iChecksleft = 5 ; number of condition checks TBDEvent OnUpdate()float fTime = 10.0 ;time in seconds till it starts OnUpdate againif PlayerRef.HasEffectKeyword(aleInjuryKW) && PlayerRef.GetActorValuePercentage("Health") > 0.99 ; has an injury and has 100 percent healthiChecksLeft-=1 ; remove 1 from the ichecksleft countRegisterForSingleUpdate(fTime)endifif iChecksLeft > 0elseDebug.Notification("Injuries Healed")ale_InjuryFullHeal.cast(PlayerRef); cures player of injury and injurykeywordendifEndEvent that and those conditions listed are not really a true/false so didnt use the bool, I guess I didnt know how to set those as a bool So whats wrong with the above? not starting up at all? (see post 8 then check spoiler in this post) not providing the right conditions? maybe because PlayerRef isnt defined anymore due to it not being PlayerRef = akTarget ? Link to comment Share on other sites More sharing options...
foamyesque Posted June 1, 2018 Share Posted June 1, 2018 (edited) ugh, apparently activemagiceffects cant have on update events? They absolutely can. Events can't be nested inside events, however, just like you can't code one function inside another function. You need to write them separately. You can however call events and functions from within other events. Your code could look like this, for example: Actor PlayerRef = none int iCheckCount = 0 float fPollTime = 20.0 float fMaxTime = 600.0 Event OnEffectStart(Actor akTarget, Actor akCaster) PlayerRef = AkTarget iCheckCount = (fMaxTime / fPollTime) as int if PlayerRef.HasEffectKeyword(aleInjuryKW) RegisterForSingleUpdate(fPollTime) endif EndEvent Event OnUpdate() if PlayerRef.HasEffectKeyword(aleInjuryKW) && PlayerRef.GetActorValuePercentage("Health") > 0.99 if iCheckCount > 0 iCheckCount -= 1 RegisterForSingleUpdate(fPollTime) else Debug.Notification("Injuries Healed") ale_InjuryFullHeal.cast(PlayerRef) endif endif EndEvent However looking at what this code is apparently trying to do there may be another approach: Offload the polling to the spell itself. Edited June 1, 2018 by foamyesque Link to comment Share on other sites More sharing options...
Recommended Posts