Jump to content

[LE] Papyrus, avoiding multiple IF chain


Recommended Posts

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 && condition2
Utility.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
EndIf
EndIf
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 true
increase a count int by 1
then wait x seconds
THEN repeat else if conditions fail reset count
when count is 10
do thing i need and end event
is that somthing that will work? any examples?
Link to comment
Share on other sites

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 by B1gBadDaddy
Link to comment
Share on other sites

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

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

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 by Rasikko
Link to comment
Share on other sites

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 by foamyesque
Link to comment
Share on other sites

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 by B1gBadDaddy
Link to comment
Share on other sites

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 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

 

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

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 activemagiceffect

SPELL Property ale_InjuryFullHeal Auto
Actor Property PlayerRef Auto
Keyword Property aleInjuryKW Auto

int iChecksleft = 5 ; number of condition checks TBD

Event OnUpdate()

float fTime = 10.0 ;time in seconds till it starts OnUpdate again

if PlayerRef.HasEffectKeyword(aleInjuryKW) && PlayerRef.GetActorValuePercentage("Health") > 0.99 ; has an injury and has 100 percent health
iChecksLeft-=1 ; remove 1 from the ichecksleft count
RegisterForSingleUpdate(fTime)
endif

if iChecksLeft > 0
else
Debug.Notification("Injuries Healed")
ale_InjuryFullHeal.cast(PlayerRef); cures player of injury and injurykeyword
endif

EndEvent

 

 

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

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 by foamyesque
Link to comment
Share on other sites

  • Recently Browsing   0 members

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