Variables that get reset to zero when game restarts

Post » Sun May 29, 2011 1:23 am

Okay, I have one mod that needs to run specific functions before a mod based on it runs. How do I create a variable that is not set or set to zero when the game starts? Then the main mod can run its init routine, then the mods based on it can run theirs. I though about using "fQuestDelayTime" but was not sure it would reset to 5 on restart. Does it? Is there syncing mechanism somewhere already?
User avatar
Steeeph
 
Posts: 3443
Joined: Wed Apr 04, 2007 8:28 am

Post » Sat May 28, 2011 10:59 pm

I have a setup that I think is similar to yours. I have a master script that runs at game start, and depending on INI settings initializes between one and five other scripts.
Scripts can share variables, even across mods (I think), which might be the easiest way to do what you're wanting to do. In my example:

Quest "BFGMaster" runs script "BFGMasterScript" at startup.
BFGMasterScript needs to tell BFGScript01, which is connected to quest BFG01, when it has finished.

At the end of BFGMasterScript, I do the following:
Let BFG01.Ready == 1

This sets the variable called Ready in BFGScript01, NOT BFGMasterScript, to 1. Then, it's just a matter of using If Ready == 1 in BFGScript01.

Hope that explanation makes sense. If not let me know.
User avatar
Irmacuba
 
Posts: 3531
Joined: Sat Mar 31, 2007 2:54 am

Post » Sat May 28, 2011 5:33 pm

I have a setup that I think is similar to yours. I have a master script that runs at game start, and depending on INI settings initializes between one and five other scripts.
Scripts can share variables, even across mods (I think), which might be the easiest way to do what you're wanting to do. In my example:

Quest "BFGMaster" runs script "BFGMasterScript" at startup.
BFGMasterScript needs to tell BFGScript01, which is connected to quest BFG01, when it has finished.

At the end of BFGMasterScript, I do the following:
Let BFG01.Ready == 1

This sets the variable called Ready in BFGScript01, NOT BFGMasterScript, to 1. Then, it's just a matter of using If Ready == 1 in BFGScript01.

Hope that explanation makes sense. If not let me know.


Yes, that explanation makes sense. However, I need the value to be volatile as the script needs to run each time it starts the game. The value needs to be reset to 0 when the game starts up again.
User avatar
Danny Blight
 
Posts: 3400
Joined: Wed Jun 27, 2007 11:30 am

Post » Sat May 28, 2011 6:50 pm

"fQuestDelayTime" won't work. It stays at its value once set. Dang it.
User avatar
Cheville Thompson
 
Posts: 3404
Joined: Sun Mar 25, 2007 2:33 pm

Post » Sat May 28, 2011 12:02 pm

Okay, I figured it out! I created an array var that when initialized is cleared out to size zero. When a mod of this mod runs it will check to see if a key it sets in this variable is set. If it is set then it will not run its init until it sees the key disappear from the array. This could be a problem on the first run, but I will also leave instructions to set the priority lower. Another thing that could be done is to have the mod check to see if arrays it changes have their values set in them. If not then you know it was initialized as well.
User avatar
glot
 
Posts: 3297
Joined: Mon Jul 17, 2006 1:41 pm

Post » Sat May 28, 2011 12:26 pm

Okay, I figured it out! I created an array var that when initialized is cleared out to size zero. When a mod of this mod runs it will check to see if a key it sets in this variable is set. If it is set then it will not run its init until it sees the key disappear from the array. This could be a problem on the first run, but I will also leave instructions to set the priority lower. Another thing that could be done is to have the mod check to see if arrays it changes have their values set in them. If not then you know it was initialized as well.


Ooh, solved this issue too:
array_var modFlags...; clear mod valuesar_Resize modFlags 0let modFlags["initialized"] := 1 


This is in a quest BTW. So, the way it now works is this:
1. On first run there will not be anything in the array.
2. When the main mod loads it clears the size and adds one entry "initialized".
3. The other mods look for their own key and check for the entry "initialized".
4. If "initialized" is present and their own key is not then it is time to initialize themselves.

One problem is if the main mod has run with no other mods loaded then "initialized" would be set upon restarting the saved game. One way to help with that is add another entry when the game is saved. Like "saved". Then the others mods would see that and wait until the "saved" disappears.
User avatar
Beth Belcher
 
Posts: 3393
Joined: Tue Jun 13, 2006 1:39 pm

Post » Sat May 28, 2011 11:31 pm

For this sort of situation I generally use a block something like this:

short readyif GetGameLoaded    set Master.ready to 0    set ready to 0    returnelseif ready == 0    if (Master.ready)        set ready to 1    else        return    endifendif


And in the master script:

short readyif GetGameLoaded    << do stuff >>    set ready to 1elseif ready == 0    set ready to 1endif


The advantage of this method is that the Master script doesn't need to know that your dependent script exists, which is essential if you're trying to build a platform which any arbitrary mod can access without compatibility issues. The "ready" variable controls nothing in the master script, and if it's ever zero after the first frame it's just set to 1; this ensures that late-starting child scripts and differences in fQuestDelayTime don't cause the init block to run more than once. Similarly, the child only checks master status if its local ready state is zero; other child scripts setting the master's ready state to zero on later flames won't cause any hiccups.


Now, there's another theoretical possibility with OBSE 0018+. I haven't tried it yet, but a conversation with Scruggs suggests that it should work. GetGameLoaded runs once per script, and that includes function scripts, which means you should be able to do this:

ScN myGameLoadInitbegin function {}    if GetGameLoaded        << do stuff >>   endifend


And now both your master and dependent scripts need only do the following:

if GetGameLoaded    call myGameLoadInitendif

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

Post » Sat May 28, 2011 9:49 pm

Now, there's another theoretical possibility with OBSE 0018+. I haven't tried it yet, but a conversation with Scruggs suggests that it should work. GetGameLoaded runs once per script, and that includes function scripts, which means you should be able to do this:
Pretty sure that won't work, or rather, it'll return true every time. Keep in mind that a function, when called, is its own instance of the function script.
User avatar
Milagros Osorio
 
Posts: 3426
Joined: Fri Aug 25, 2006 4:33 pm

Post » Sat May 28, 2011 4:06 pm

Pretty sure that won't work, or rather, it'll return true every time. Keep in mind that a function, when called, is its own instance of the function script.

Like I said, I haven't tried it yet, but I asked Scruggs about exactly that. More than once, in fact, just to make sure the question was clear. He said once per script, and that includes function scripts.

Wow... we're both terribly lazy, aren't we. I'm gonna go check this. Back in a moment. :)

Verdict: it works!

ScN InitFunctionbegin function {}	if GetGameLoaded		printc "***INITIALIZED***"	endifendScN dikeScriptbegin GameMode	call InitFunctionendScN JaneScriptbegin GameMode	call InitFunctionend

Quests dike and Jane have dikeScript and JaneScript attached and are set to run on game start. After 30 seconds, the console shows ***INITIALIZED*** once and only once, after both scripts will have fired several times.

Edit: Now I remember Scruggsy's explanation of why this works... the state data is stored externally. GetGameLoaded keeps a list of scripts which have called it, by FormID. It doesn't know about script instances at all. Similarly, GetGameLoaded fires once per object script, NOT once per scripted object.
User avatar
Vahpie
 
Posts: 3447
Joined: Sat Aug 26, 2006 5:07 pm

Post » Sat May 28, 2011 8:44 pm

Like I said, I haven't tried it yet, but I asked Scruggs about exactly that. More than once, in fact, just to make sure the question was clear. He said once per script, and that includes function scripts.

Wow... we're both terribly lazy, aren't we. I'm gonna go check this. Back in a moment. :)

Verdict: it works!
Hehe, just checked it myself too. I gotta say, this sure brings alot of new tricks into play. :D

Edit: Now I remember Scruggsy's explanation of why this works... the state data is stored externally. GetGameLoaded keeps a list of scripts which have called it, by FormID. It doesn't know about script instances at all. Similarly, GetGameLoaded fires once per object script, NOT once per scripted object.
It's a bit of a duh moment as I specifically looked at how GetGameLoaded and GetGameRestarted worked when I first delved into the OBSE src. :facepalm:
User avatar
Soraya Davy
 
Posts: 3377
Joined: Sat Aug 05, 2006 10:53 pm

Post » Sun May 29, 2011 12:14 am

The problem which your code may solve is this:
; master modar_Resize dataArray 0let dataArray["somedata"] := "blah"; slave modlet dataArray["somedata2"] := "blah blah"


How does the slave know that the master has already cleared out the array? One way to check is to look for data and then look for its own data. But this does not solve the problem of the master loading the data from save, the slave writes its data, then the master wipes it out. Can this be solved by quest priority and load order?
User avatar
JD FROM HELL
 
Posts: 3473
Joined: Thu Aug 24, 2006 1:54 am

Post » Sun May 29, 2011 2:12 am

The problem with the "ready" flag is it is stored in the save. If the game loads, sets ths ready flag to what it was set to when saved, and the slave runs before the master then it will add its array entries, then the master will squash them.

One way I though about accomplishing this is a new ring formid. When the master starts it will enchant the ring with something. When the game saves it will not save the enchantment because it is not a cloned formid. Reloading and the enchantment is gone. I can put functions around this to have the slave check for the enchantment. A hack, yes, but it should work with no hiccups for what I need.
User avatar
Lance Vannortwick
 
Posts: 3479
Joined: Thu Sep 27, 2007 5:30 pm

Post » Sat May 28, 2011 11:31 pm

Okay I think I got this:
scn WOLUtlGetMasterInitialized; checks for the master plugin being initialized; formid WOLReloadFlagRing is enchanted when the master initializes itselfshort enchantedref enchantmentbegin Function{}	let enchantment := GetEnchantment WOLReloadFlagRing	let enchanted := 0	if( enchantment )		let enchanted := 1	endif	SetFunctionValue enchantedendscn WOLUtlSetMasterInitialized; enchants formid WOLReloadFlagRing to act as a volatile flagbegin Function{}	SetEnchantment EnAppCaster WOLReloadFlagRingend


As long as these are called in the same place in the master and slave. ie in GetGameReloaded or GetGameRestarted "if" statements. This will work as long as those are not mixed up. So, it provides a nice way to check for both situations and provides the volatility I was looking for in a variable.
User avatar
no_excuse
 
Posts: 3380
Joined: Sun Jul 16, 2006 3:56 am

Post » Sat May 28, 2011 7:14 pm

As mentioned above: if you put a GetGameLoaded around all the code in those functions, you don't have to worry about it in scripts which reference them. The function can be called any number of times but the code will only run once.
User avatar
Alyesha Neufeld
 
Posts: 3421
Joined: Fri Jan 19, 2007 10:45 am

Post » Sat May 28, 2011 6:10 pm

As mentioned above: if you put a GetGameLoaded around all the code in those functions, you don't have to worry about it in scripts which reference them. The function can be called any number of times but the code will only run once.

Yes, but I am not seeing how GetGameLoaded alone will ensure the slave code runs after the master code.

Is the idea that the slave runs the master init code if it runs first? Okay, I was not connecting the dots there. That is a really interesting way to look at doing that. One problem is creating arrays. The function that initializes will have to be aware of the arrays in the quest script. I generally try to call functions and pass an array in. That way the function is more generic. I will have to think about this approach.

Thanks for the clarification.
User avatar
Enie van Bied
 
Posts: 3350
Joined: Sun Apr 22, 2007 11:47 pm

Post » Sat May 28, 2011 3:50 pm

Why not simply give each script that needs to initialize its own GetGameLoaded block, then?
User avatar
Matt Terry
 
Posts: 3453
Joined: Sun May 13, 2007 10:58 am

Post » Sat May 28, 2011 11:53 am

Why not simply give each script that needs to initialize its own GetGameLoaded block, then?

The problem is I have two mods: a master mod and a slave mod. The master mod has an array that it has to clear out and add data to on initialization. When the slave mod runs it adds data to this array. If the slave runs first then the data is wiped out by the master mod. The point is to have the master init code run first no matter what.

tejon, pointed out that if I create a function in the master mod that has a getgameloaded block inside it, then it can be run by the master or slave. No matter who runs it then it runs once and doesn't wipe out data by the slave mod.

tejon,
I have one question on this. If the slave runs a master function does OBSE see the data as belonging to the slave or the master? Or does it go by ownership of the function or reference? This could be important if the slave points to its own functions when it adds data to an array. This is why it would be very important for the array to be cleared out on each game load too! It would be bad to call a function on a slave mod that doesn't exist.
User avatar
Grace Francis
 
Posts: 3431
Joined: Wed Jul 19, 2006 2:51 pm

Post » Sat May 28, 2011 8:58 pm

Oh, I didn't realize you were dealing with two mods; sorry about that.

Anyway, I'm somewhat confused by your new question. In general, OBSE will be looking at two things - any objects or references involved (that includes scripts, if you're storing a script in a ref variable or whatever), and the calling script. If a slave mod has a script that calls a function in the master mod, OBSE will see exactly that - it will see the script that's making the call (and be able to tell which mod it comes from), and it will also see that the function itself is a script in the master mod. If the slave mod puts data into an array in the master mod's script, then OBSE will see the array as a part of the master mod's script - but if there's an object in the array from the slave mod, OBSE will see that it comes from the slave mod.

Now one thing I'm not sure about is mod resolution. Typically Oblivion will 'fix' any and all mod indexes (the first byte of a FormID), to reflect changes in load order. If a mod is removed, that mod's forms (those FormIDs whose first byte was the mod's previous load order position) are not loaded by the game (indeed, they couldn't be, as the mod's no longer there to reference). This is what happens when someone makes a "clean save", for example.

If OBSE supports ref variables in arrays (and I can't recall if it does), then it should be performing mod resolution on those forms, too - and therefore remove any that come from unloaded mods. If it does not, any formIDs stored in an array become unusable after a load, because their mod indices (the first byte) might be incorrect, and they may therefore refer to an incorrect (or non-existent) form, even if the mod is still present.
User avatar
Chris Ellis
 
Posts: 3447
Joined: Thu Jul 26, 2007 10:00 am


Return to IV - Oblivion