How to know whether a firsttime {} has triggered or not?

Hello again!

Once again I'm working on updating my SaveLoadCode library and hit a snag. Right now, my library has no way of knowing whether or not a firsttime {} otherwise {} pair has triggered and I am looking to remedy that so they don't re-trigger when a player loads their game with my library.

I've figured out that the room.beforefirstenter and room.firstenter scripts trigger off of the room.visited attribute, the text processor commands {once: } and {notfirst: } functions key off of the game.textprocessor_seen dictionary parameter, but I can't seem to find how the game knows whether afirsttime {} and otherwise {} script has already triggered?

Is there some other attribute that this information is stored under that I'm missing? Is it buried somewhere in the java code, and if so is there a way I can save/update it?

Hoping someone with better knowledge of the Quest source code can help me figure this out...


Is there some other attribute that this information is stored under that I'm missing? Is it buried somewhere in the java code, and if so is there a way I can save/update it?

It's not in the code anywhere, unfortunately. firsttime is a hard coded function, so is in the C# code, and would need a custom version of Quest to change how it works.

You can check if a firsttime block has fired – if you convert a script attribute to a string, you will see that any firsttime blocks are actually removed from the script after they fire (and otherwise blocks are replaced by their contents, without the otherwise keyword)
This change is actually made when the script attribute is prepared for saving – I'm not sure whether the change will be visible to your code after saving, or only after loading a saved game. This may differ between desktop and online versions of the player.


Interesting... Definitely sounds more efficient from a code standpoint, but unfortunately makes it harder for me...

Was thinking about this for a bit, and I think one way I could get past this with my library would be to provide new firsttime/otherwise functions that would be compatible with my library. Maybe something like...

firsttime_saveload (uniqueid) {firstscript}
  if (not HasAttribute(game, "SaveLoadCode_firsttimetrigger")) {
    // Make it a string list so SaveLoadCode can more easily parse it
    game.SaveLoadCode_firsttimetrigger = NewStringList()
  }
  if (not TypeOf(uniqueid)="string") {
    uniqueid = ToString(uniqueid)
  }
  if (not ListContains(game.SaveLoadCode_firsttimetrigger, uniqueid) {
    // Then this should be the first time this is triggering
    list add (game.SaveLoadCode_firsttimetrigger, uniqueid)
    invoke(firstscript)
  }
  // Else, the script tied to this uniqueid has already triggered...
otherwise_saveload (uniqueid) {otherscript}
  if (not HasAttribute(game, "SaveLoadCode_firsttimetrigger")) {
    // Make it a string list so SaveLoadCode can more easily parse it
    game.SaveLoadCode_firsttimetrigger = NewStringList()
  }
  if (not TypeOf(uniqueid)="string") {
    uniqueid = ToString(uniqueid)
  }
  if (ListContains(game.SaveLoadCode_firsttimetrigger, uniqueid) {
    // Then the 'firsttime' paired to this 'otherwise' should have already triggered, run this otherwise
    invoke(otherscript)
  }

The users would have to CTRL+F through their code and replace any firsttime/otherwise instances with these new firsttime_saveload () and otherwise_saveload () functions, and provide each pair with a unique id in order to remain compatible, but then as long as my library saves the game.SaveLoadCode_firsttimetrigger parameter (if it exists at time of saving), then as far as I can tell it should work...

This is the most efficient solution I could come up with at this time, but I'm open to suggestions if people have other ideas.


I might suggest allowing a 'default' ID to make those functions easier to use (so game designers don't have to keep track of what IDs they already used).

For example, you could have something like:

// If there's only one argument, use it as the script as well as (a stringified form) as the ID
if (not IsDefined("firstscript")) {
  firstscript = uniqueid
}

This would work fine unless the same script is used in two firsttime functions, in which case the user can still give their own ID.
Or to reduce the size of save files (saving massive strings in the list), you could hash the script in some way.

For the otherwise function, I've not tried it but I think you might have an issue. If you have a firsttime followed immediately by an otherwise with the same ID, it will run both, because the firsttime has already run before the otherwise is reached.

You could resolve this by having two separate lists, for firsttime and otherwise IDs. That way, your otherwise function will skip the first time it is reached, separate from the firsttime function.

Alternatively, you could do it the way that the text processor else is handled: Have a global boolean, something like game.SaveLoadCode_run_otherwise which is set to true or false after running (or not) a firsttime script. Then the otherwise function could just check this – running only if the last-encountered firsttime didn't. Although this would prevent you from having an otherwise without a firsttime, possibly removing one of the advantages over the built-in system.

If you want to have otherwise work without firsttime, the (increasingly ugly) workaround might be making the list a dictionary, with its values true or false depending whether the corresponding otherwise should run. The first time a firsttime is encountered, it would set it to false. Subsequent times, it would remove it from the list and add it again as true. The firsttime function will run its script based on the presence or absence of a specific key in the dictionary; while the otherwise function would use the true/false value.

Hope that makes some kind of sense.


Log in to post a reply.

Support

Forums